夜雨晨风丶
本帖最后由 夜雨晨风丶 于 2023-3-12 17:53 编辑
  1. @Override
  2.     public void onEnable() {
  3.         Instance = this;
  4.         //检测是否存在插件文件夹,如果没有则进行创建
  5.         File dataFolder = getDataFolder();
  6.         if (!dataFolder.exists()) {
  7.             dataFolder.mkdirs();
  8.         }
  9.         //检查存储模式,如果是YAML则创建items.yml文件,如果是MySQL则跳过创建文件
  10.         if (getConfig().getString("saveMode").equalsIgnoreCase("yaml")) {
  11.             createItemsFile();
  12.         } else {
  13.             // 实例化SqlStorage并传入插件实例
  14.             SqlStorage sql = new SqlStorage(this);
  15.             sql.saveDespawnItemsToMySQL();
  16.         }
  17.         //保存默认配置并加载数据
  18.         saveDefaultConfig();
  19.         ConfigFile.loadConfig();
复制代码


插件第一次加载数据库时会执行这个方法建立连接建表
  1. public void saveDespawnItemsToMySQL() {
  2.         Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
  3.             Connection conn = null;
  4.             PreparedStatement pstmt = null;
  5.             try {
  6.                 //连接到MySQL数据库
  7.                 Class.forName("com.mysql.jdbc.Driver");
  8.                 conn = DriverManager.getConnection("jdbc:mysql://" + host + ":" + port + "/" + database + "?useSSL=false", username, password);
  9.                 //创建表
  10.                 pstmt = conn.prepareStatement("CREATE TABLE IF NOT EXISTS despawn_items (id INT NOT NULL AUTO_INCREMENT, item_data BLOB, PRIMARY KEY (id))");
  11.                 pstmt.executeUpdate();

  12.                 //插入每个ItemStack
  13.                 for (ItemStack itemStack : ConfigFile.despawnItems) {
  14.                     pstmt = conn.prepareStatement("INSERT INTO despawn_items (item_data) VALUES (?)");
  15.                     ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
  16.                     BukkitObjectOutputStream dataOutput = new BukkitObjectOutputStream(outputStream);
  17.                     dataOutput.writeObject(itemStack);
  18.                     dataOutput.flush();
  19.                     dataOutput.close();
  20.                     pstmt.setBytes(1, outputStream.toByteArray());
  21.                     pstmt.executeUpdate();
  22.                 }
  23.             } catch (SQLException | ClassNotFoundException | IOException e) {
  24.                 e.printStackTrace();
  25.             } finally {
  26.                 //关闭连接
  27.                 try {
  28.                     if (pstmt != null) pstmt.close();
  29.                     if (conn != null) conn.close();
  30.                 } catch (SQLException e) {
  31.                     e.printStackTrace();
  32.                 }
  33.             }
  34.         });
  35.     }
复制代码
但是加载到ConfigFile.loadConfig()时就产生报错了


  1. public static void loadConfig() {
  2.         ItemsProtect ip = ItemsProtect.getInstance();
  3.         ip.reloadConfig();
  4.         ...
  5.         if (saveMode.equalsIgnoreCase("yaml")) {
  6.             loadDespawnItems(ip);
  7.         } else {
  8.             sql.loadDespawnItemsFromMySQL(items -> {
  9.                 // 处理物品列表
  10.                 ConfigFile.despawnItems = items;
  11.             });
  12.         }

  13.     }
复制代码
  1. public void loadDespawnItemsFromMySQL(Consumer<List<ItemStack>> callback) {
  2.         Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
  3.             List<ItemStack> itemList = new ArrayList<>();
  4.             Connection conn = null;
  5.             PreparedStatement pstmt = null;
  6.             ResultSet rs = null;
  7.             try {
  8.                 //连接到MySQL数据库
  9.                 Class.forName("com.mysql.jdbc.Driver");
  10.                 conn = DriverManager.getConnection("jdbc:mysql://" + host + ":" + port + "/" + database + "?useSSL=false", username, password);
  11.                 //从表中检索每个ItemStack
  12.                 pstmt = conn.prepareStatement("SELECT item_data FROM despawn_items");
  13.                 rs = pstmt.executeQuery();
  14.                 while (rs.next()) {
  15.                     byte[] bytes = rs.getBytes("item_data");
  16.                     ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
  17.                     BukkitObjectInputStream dataInput = new BukkitObjectInputStream(inputStream);
  18.                     ItemStack itemStack = (ItemStack) dataInput.readObject();
  19.                     itemList.add(itemStack);
  20.                 }
  21.                 //将检索到的ItemStacks赋值给ConfigFile中的despawnItems
  22.                 ConfigFile.despawnItems = itemList;
  23.                 //执行回调函数
  24.                 callback.accept(itemList);
  25.             } catch (SQLException | IOException | ClassNotFoundException e) {
  26.                 e.printStackTrace();
  27.             } finally {
  28.                 //关闭连接和资源
  29.                 try {
  30.                     if (rs != null) rs.close();
  31.                     if (pstmt != null) pstmt.close();
  32.                     if (conn != null) conn.close();
  33.                 } catch (SQLException e) {
  34.                     e.printStackTrace();
  35.                 }
  36.             }
  37.         });
  38.     }
复制代码


报错如下
  1. [05:01:10] [Craft Scheduler Thread - 6/WARN]: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'noinvput.despawn_items' doesn't exist
  2. [05:01:10] [Craft Scheduler Thread - 6/WARN]:         at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
  3. [05:01:10] [Craft Scheduler Thread - 6/WARN]:         at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
  4. [05:01:10] [Craft Scheduler Thread - 6/WARN]:         at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
  5. [05:01:10] [Craft Scheduler Thread - 6/WARN]:         at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
  6. [05:01:10] [Craft Scheduler Thread - 6/WARN]:         at com.mysql.jdbc.Util.handleNewInstance(Util.java:425)
  7. [05:01:10] [Craft Scheduler Thread - 6/WARN]:         at com.mysql.jdbc.Util.getInstance(Util.java:408)
  8. [05:01:10] [Craft Scheduler Thread - 6/WARN]:         at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:944)
  9. [05:01:10] [Craft Scheduler Thread - 6/WARN]:         at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3973)
  10. [05:01:10] [Craft Scheduler Thread - 6/WARN]:         at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3909)
  11. [05:01:10] [Craft Scheduler Thread - 6/WARN]:         at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2527)
  12. [05:01:10] [Craft Scheduler Thread - 6/WARN]:         at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2680)
  13. [05:01:10] [Craft Scheduler Thread - 6/WARN]:         at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2484)
  14. [05:01:10] [Craft Scheduler Thread - 6/WARN]:         at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1858)
  15. [05:01:10] [Craft Scheduler Thread - 6/WARN]:         at com.mysql.jdbc.PreparedStatement.executeQuery(PreparedStatement.java:1966)
  16. [05:01:10] [Craft Scheduler Thread - 6/WARN]:         at cordori.itemsprotect.utils.SqlStorage.lambda$loadDespawnItemsFromMySQL$1(SqlStorage.java:84)
  17. [05:01:10] [Craft Scheduler Thread - 6/WARN]:         at org.bukkit.craftbukkit.v1_12_R1.scheduler.CraftTask.run(CraftTask.java:64)
  18. [05:01:10] [Craft Scheduler Thread - 6/WARN]:         at org.bukkit.craftbukkit.v1_12_R1.scheduler.CraftAsyncTask.run(CraftAsyncTask.java:52)
  19. [05:01:10] [Craft Scheduler Thread - 6/WARN]:         at com.destroystokyo.paper.ServerSchedulerReportingWrapper.run(ServerSchedulerReportingWrapper.java:22)
  20. [05:01:10] [Craft Scheduler Thread - 6/WARN]:         at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
  21. [05:01:10] [Craft Scheduler Thread - 6/WARN]:         at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
  22. [05:01:10] [Craft Scheduler Thread - 6/WARN]:         at java.lang.Thread.run(Thread.java:750)
  23. [05:01:10] [Craft Scheduler Thread - 7/WARN]: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'noinvput.despawn_items' doesn't exist
  24. [05:01:10] [Craft Scheduler Thread - 7/WARN]:         at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
  25. [05:01:10] [Craft Scheduler Thread - 7/WARN]:         at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
  26. [05:01:10] [Craft Scheduler Thread - 7/WARN]:         at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
  27. [05:01:10] [Craft Scheduler Thread - 7/WARN]:         at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
  28. [05:01:10] [Craft Scheduler Thread - 7/WARN]:         at com.mysql.jdbc.Util.handleNewInstance(Util.java:425)
  29. [05:01:10] [Craft Scheduler Thread - 7/WARN]:         at com.mysql.jdbc.Util.getInstance(Util.java:408)
  30. [05:01:10] [Craft Scheduler Thread - 7/WARN]:         at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:944)
  31. [05:01:10] [Craft Scheduler Thread - 7/WARN]:         at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3973)
  32. [05:01:10] [Craft Scheduler Thread - 7/WARN]:         at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3909)
  33. [05:01:10] [Craft Scheduler Thread - 7/WARN]:         at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2527)
  34. [05:01:10] [Craft Scheduler Thread - 7/WARN]:         at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2680)
  35. [05:01:10] [Craft Scheduler Thread - 7/WARN]:         at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2484)
  36. [05:01:10] [Craft Scheduler Thread - 7/WARN]:         at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1858)
  37. [05:01:10] [Craft Scheduler Thread - 7/WARN]:         at com.mysql.jdbc.PreparedStatement.executeQuery(PreparedStatement.java:1966)
  38. [05:01:10] [Craft Scheduler Thread - 7/WARN]:         at cordori.itemsprotect.utils.SqlStorage.lambda$loadDespawnItemsFromMySQL$1(SqlStorage.java:84)
  39. [05:01:10] [Craft Scheduler Thread - 7/WARN]:         at org.bukkit.craftbukkit.v1_12_R1.scheduler.CraftTask.run(CraftTask.java:64)
  40. [05:01:10] [Craft Scheduler Thread - 7/WARN]:         at org.bukkit.craftbukkit.v1_12_R1.scheduler.CraftAsyncTask.run(CraftAsyncTask.java:52)
  41. [05:01:10] [Craft Scheduler Thread - 7/WARN]:         at com.destroystokyo.paper.ServerSchedulerReportingWrapper.run(ServerSchedulerReportingWrapper.java:22)
  42. [05:01:10] [Craft Scheduler Thread - 7/WARN]:         at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
  43. [05:01:10] [Craft Scheduler Thread - 7/WARN]:         at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
  44. [05:01:10] [Craft Scheduler Thread - 7/WARN]:         at java.lang.Thread.run(Thread.java:750)
复制代码


是我哪里写错了吗,表确实在插件加载时创建好了,但是在创建好之后进行的第一次加载表数据不知道是哪出了问题。如果代码提供的不够详细可以直接查看源码 github

名副其实
本帖最后由 名副其实 于 2023-3-12 08:55 编辑

所请求的表在数据库文件中不存在。
检查一下你的代码是不是没创建这个名叫 noinvput.despawn_items 的表。

呃呃,很有可能是链接数据库或者创建表的时候没到位,你检查一下你那两个 sql 命令是不是写错了

美味的曲奇
异步出了问题

你在
ConfigFile.loadConfig(); 前调用了 sql.saveDespawnItemsToMySQL(); 按顺序来说应该是先创建的,但异步调度器执行任务不提供顺序承诺,并无法保证 ConfigFile.loadConfig() 一定是在数据表创建后执行,因为SQL操作需要网络IO,很多时候都会耗时更长。
bukkit本身生命周期设计,插件Enable逻辑执行时调度器并没有开始调度,也就是说saveDespawnItemsToMySQL 的逻辑实际是在插件 enable 逻辑之后才会执行,查看表在创建表前执行了,必然会报错找不到表

也就是说你理解的执行顺序是
---
enable语句块:
        1. 创建表
        2. 调用表
---
但实际是
---
enable语句块:
        1. 提交创建表任务
        2. 调用表
3. 执行创建表任务
---
可以通过在saveDespawnItemsToMySQL中加入一句log输出语句来验证这一点
所以在服务器启动时会报错,但完全启动后数据表存在
解决方法也很多,将读取表作为异步任务的回调,通过一个标识位,或是设计任务队列来保证表的调用一定在创建逻辑之后执行等

夜雨晨风丶

我还是有点点想不明白就是我创建表时设置个标识符isTableCreated = true;我想判断为true时才继续执行下去操作,但是我这样写好像又会把主线程一直堵着,猪脑过载了,想不明白怎么用
  1. @Override
  2.     public void onEnable() {
  3.         Instance = this;
  4.         //检测是否存在插件文件夹,如果没有则进行创建
  5.         File dataFolder = getDataFolder();
  6.         if (!dataFolder.exists()) {
  7.             dataFolder.mkdirs();
  8.         }
  9.         //检查存储模式,如果是YAML则创建items.yml文件,如果是MySQL则跳过创建文件
  10.         if (getConfig().getString("saveMode").equalsIgnoreCase("yaml")) {
  11.             createItemsFile();
  12.         } else {
  13.             // 实例化SqlStorage并传入插件实例
  14.             SqlStorage sql = new SqlStorage(this);
  15.             sql.saveDespawnItemsToMySQL();
  16.             while(!sql.isTableCreated()) {
  17.                 try {
  18.                     Thread.sleep(1000);
  19.                 } catch (InterruptedException e) {
  20.                     throw new RuntimeException(e);
  21.                 }
  22.             }
  23.         }
  24.         //保存默认配置并加载数据
  25.         saveDefaultConfig();
  26.         ConfigFile.loadConfig();
复制代码

美味的曲奇
夜雨晨风丶 发表于 2023-3-12 18:17
我还是有点点想不明白就是我创建表时设置个标识符isTableCreated = true;我想判断为true时才继续 ...

原因很简单:
调度器要在Enable语句块之后才开始进行调度

Bukkit的生命周期如下
LOAD        依次调用 onLoad() 方法
ENABLE        依次调用所有插件的 onEnable() 方法
ACTIVE        调度器开始执行

在你的 onEnable 方法返回之前调度器是不会执行的,所以这样使用标识符没有意义
如果要使用标识符就把 else 块及其以后的语句全部异步出去
但逻辑会很复杂,更推荐在创建表函数方法中递入回调函数

第一页 上一页 下一页 最后一页