You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java获取GUI用户输入写入NetBeans Derby数据库失败排查

Derby数据库写入无记录问题修复方案

你的代码存在多处核心错误,直接导致数据无法落盘,逐个修正即可:

核心问题清单

  1. INSERT语句语法完全错误
    你当前写的SQL有两个致命问题:
    • 用单引号包裹表名、列名:SQL中单引号是字符串常量的标记,不能用来包裹标识符(表名、列名),Derby会直接把'PLAYERINFO'识别为字符串而非表名,报语法错误
    • VALUES子句写死了1,2,3,4,没有留?占位符:PreparedStatement的setXxx方法只会替换SQL里的?占位符,你写死常量的情况下,后续set参数的操作要么报参数数量不匹配错误,要么完全不生效
  2. 操作的表名不匹配
    你插入数据的目标表是PLAYERINFO,但调用表检查方法时传的参数是PLAYERS,相当于每次写入前检查/删除的是一张和业务无关的表,真正要写入的PLAYERINFO如果没提前创建,执行INSERT时会直接报表不存在的错误。
  3. 异常被静默吞掉
    你的catch块只打印了ex.getMessage(),没有输出完整异常栈,Swing事件线程里的控制台输出很容易被忽略,SQL语法错、表不存在、主键冲突这些报错你根本看不到,表面上看就是点了按钮没反应、数据没写入。
  4. 事务未提交
    如果你自定义的DBConnection类里关闭了自动提交(autoCommit=false),执行完executeUpdate()后必须手动调用conn.commit()才能把数据刷到磁盘,否则连接关闭时事务会自动回滚,数据不会持久化。
  5. 主键逻辑错误+危险的删表逻辑
    • playerID是方法内局部变量,每次调用方法都初始化为0,第一次插入ID是0,后续每次插入ID都是1,主键重复直接报错
    • 原来的表检查逻辑是找到同名表就直接删除,就算你前面的逻辑全对,每次跑程序都会把之前存的表删掉,永远看不到历史数据。
  6. 资源未正确释放
    数据库连接、Statement、ResultSet都没有在执行完成后关闭,容易导致连接泄漏、事务锁不释放,后续写入操作直接阻塞。

修正后代码

1. 重写WritePlayerDB类

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class WritePlayerDB 
{
    private final DBConnection dbc;
    private final Connection conn;
    // 静态变量维护自增ID,更推荐把PLAYER_ID设为数据库自增主键,避免应用端计数冲突
    private static int playerID = 0;
    private static final String TARGET_TABLE = "PLAYERINFO";
    
    public WritePlayerDB() throws SQLException
    {
        dbc = new DBConnection();
        conn = dbc.getConnection();
        // 调试阶段可以打开下面这行强制开启自动提交,避免忘记提交事务
        // conn.setAutoCommit(true);
    }
    
    public void writeToPlayerDB()
    {
        // 正确的INSERT语句,用?做占位符,表名列名不用引号包裹
        String insertQuery = "INSERT INTO " + TARGET_TABLE + 
                " (PLAYER_ID, PLAYER_NAME, PRIZE_WON, CORRECT_ANSWERS) VALUES (?, ?, ?, ?)";
        // 先校验目标表是否存在,不存在则建表
        checkAndInitTable(TARGET_TABLE);
        
        // try-with-resources语法自动关闭Statement,不需要手动写close
        try (PreparedStatement ps = conn.prepareStatement(insertQuery)) {
            CorrectAnswers.correctAnswers[MillionaireGui.moneyCounter] = PrizeMoney.prizeLadder[MillionaireGui.moneyCounter];
            
            ps.setInt(1, ++playerID);
            ps.setString(2, MillionaireGui.nameField.getText().trim());
            ps.setInt(3, PrizeMoney.prizeLadder[MillionaireGui.moneyCounter]);
            ps.setInt(4, CorrectAnswers.correctAnswers[MillionaireGui.moneyCounter]);
            
            int affectedRows = ps.executeUpdate();
            System.out.println("数据写入完成,影响行数:" + affectedRows);
            
            // 非自动提交模式下手动提交事务
            if (!conn.getAutoCommit()) {
                conn.commit();
            }
            
        } 
        catch (SQLException ex) 
        {
            // 打印完整异常栈,方便定位问题
            ex.printStackTrace();
            // 出错回滚事务
            try {
                if (!conn.getAutoCommit()) conn.rollback();
            } catch (SQLException rollbackEx) {
                rollbackEx.printStackTrace();
            }
        }
    }
    
    // 原方法逻辑改成检查表是否存在,不存在则建表,禁止随便删表
    private void checkAndInitTable(String tableName) 
    {
        boolean tableExist = false;
        try (Statement stmt = conn.createStatement()) {
            DatabaseMetaData dbmd = conn.getMetaData();
            // 直接查询目标表,不需要遍历全库表
            ResultSet rs = dbmd.getTables(null, null, tableName.toUpperCase(), new String[]{"TABLE"});
            if (rs.next()) {
                tableExist = true;
            }
            rs.close();
            
            if (!tableExist) {
                String createSql = "CREATE TABLE " + tableName + " (" +
                        "PLAYER_ID INT PRIMARY KEY, " +
                        "PLAYER_NAME VARCHAR(100) NOT NULL, " +
                        "PRIZE_WON INT, " +
                        "CORRECT_ANSWERS INT)";
                stmt.executeUpdate(createSql);
                System.out.println("表" + tableName + "初始化完成");
            }
        } catch (SQLException ex) {
            ex.printStackTrace();
        }
    }
}

2. 修正GUI端调用逻辑

nameField.addActionListener(new ActionListener()
{
    public void actionPerformed(ActionEvent e)
    {
        String inputName = nameField.getText().trim();
        if (inputName.isEmpty())
        {
            blankName.setVisible(true);
            blankName.setText("PLEASE DON'T LEAVE NAME BLANK");
            return;
        }

        playerName.setPlayerName(inputName.toUpperCase());
        try {
            WritePlayerDB wdb = new WritePlayerDB();
            wdb.writeToPlayerDB();
            blankNamePanel.setVisible(false);
            introRuleScreen();
        } catch (SQLException ex) {
            blankName.setVisible(true);
            blankName.setText("数据保存失败,请查看控制台日志");
            ex.printStackTrace();
        }
    }
});

额外排查提示

  • 嵌入式Derby的数据库文件存在程序启动的工作目录下,如果你在NetBeans里切换了运行配置,可能连到了新的空数据库,要确认连接URL的路径和你查看数据的数据库路径一致
  • 调试阶段不要关闭控制台的SQL日志输出,所有数据库报错都会打印在控制台,不要忽略这些信息
  • 生产环境不要每次写入都新建数据库连接,建议把连接做成单例复用,避免连接泄漏

内容的提问来源于stack exchange,提问作者KhonsusKnight

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 23:45:35