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

JavaFX应用Inno打包后SQLite数据库创建及增删操作异常

问题根因

两种打包方案的异常核心原因一致:数据库文件存放路径错误,触发Windows系统Program Files目录的权限保护机制。

  • 代码中直接使用String dbName = "patientdata";定义相对路径访问数据库,程序启动时相对路径的根目录为JAR包所在的安装文件夹。
  • Inno Setup默认配置会将程序安装到C:\Program Files (x86)\[程序名]目录,该目录为Windows系统级受保护目录,普通用户权限仅拥有读取权限,无写入权限:
    • 方案1仅打包JAR时,程序尝试在安装目录下新建SQLite数据库文件,因无写入权限创建失败
    • 方案2将预生成的数据库文件随安装包放到安装目录时,读取存量数据不需要写入权限因此可以正常运行,但新增、删除操作需要修改数据库文件,因无写入权限执行失败
  • Eclipse开发环境下程序的工作目录为项目源码文件夹,当前登录用户对该文件夹拥有完全读写权限,因此开发阶段功能运行完全正常。
修复方案

1. 修改数据库文件存储路径

桌面应用的用户数据禁止存放在安装目录,需存储到系统为当前用户分配的可读写应用数据目录,修改getConnection()方法中数据库路径相关逻辑:

public static Connection getConnection() {      
    Connection conn = null;
    Statement stmt = null;
    try {
        String dbName  = "patientdata";
        // 获取当前用户专属的AppData应用数据目录,该目录默认拥有完全读写权限
        String appDataPath = System.getenv("APPDATA") + File.separator + "PatientManageSystem";
        File appFolder = new File(appDataPath);
        // 程序目录不存在则自动创建
        if (!appFolder.exists()) {
            appFolder.mkdirs();
        }
        File file = new File(appFolder, dbName);
        
        if(file.exists()) {
            System.out.println("patient file exists:  "+ file.getAbsolutePath());
        } else {
            System.out.println("patient file not exist: Created new one ");
        }
        
        try{
            Class.forName("org.sqlite.JDBC");
            // 连接字符串使用数据库文件的绝对路径
            conn = DriverManager.getConnection("jdbc:sqlite:"+file.getAbsolutePath());
            System.out.println("data base connection established:  "+ conn.toString());
            
            // 后续建表逻辑保持原有代码即可
            stmt = conn.createStatement();
            String pat = "CREATE TABLE if not exists newpatient " +
                           "(patientId INTEGER NOT NULL," +
                           " patientName    CHAR(50)    NOT NULL, " + 
                           " patientAge     INTEGER     NOT NULL, " + 
                           "patientGender   CHAR(10) NOT NULL,"+
                           "patientAddress  CHAR(100) NOT NULL,"+
                           "patientMobile   BIGINT(10) NOT NULL)";
            System.out.println("newpatient Table Created:  ");
            stmt.executeUpdate(pat);
            stmt.close();
                  
            stmt = conn.createStatement();
            String hist = "CREATE TABLE if not exists history " +
                             "(id INTEGER NOT NULL," +
                             " date    DATE    NOT NULL, " + 
                             " start   TIME     NOT NULL, " +                                               
                             "stop   TIME NOT NULL)";
            System.out.println("history Table Created:  ");    
            stmt.executeUpdate(hist);
            stmt.close();
                  
            Dialog<Void> pop = new Dialog<Void>();
            pop.setContentText("Data base accessed");
            pop.getDialogPane().getButtonTypes().add(ButtonType.CLOSE);
            Node closeButton = pop.getDialogPane().lookupButton(ButtonType.CLOSE);
            closeButton.setVisible(false);
            pop.showAndWait();
                                                
        }catch(SQLException tb){
            System.err.println(tb.getClass().getName() + ": " + tb.getMessage());
            Dialog<Void> pop = new Dialog<Void>();
            pop.setContentText("Data base  not accessed");
            pop.getDialogPane().getButtonTypes().add(ButtonType.CLOSE);
            Node closeButton = pop.getDialogPane().lookupButton(ButtonType.CLOSE);
            closeButton.setVisible(false);
            pop.showAndWait();
        }
    }catch(Exception e) {
        System.err.println(e.getClass().getName() + ": " + e.getMessage());
    }
    return conn;
}

代码中PatientManageSystem可替换为你自己的程序实际名称

2. 调整Inno Setup打包规则

  • 不需要将预生成的空数据库文件打入安装包,程序第一次启动时会自动在当前用户的AppData目录下创建数据库文件及对应表结构
  • 若需要给新用户预置初始数据,可将带初始数据的数据库文件作为资源打入JAR包内部,程序首次启动时检测到用户目录下无数据库文件,就将JAR包内的初始数据库复制到AppData对应目录后再建立连接,禁止直接读写JAR包内部的数据库文件——JAR为压缩包格式,内部文件不支持直接写入修改。

3. 代码优化建议

现有代码采用字符串拼接方式生成SQL语句,存在SQL注入风险,且输入内容包含单引号时会直接导致SQL执行报错,建议替换为PreparedStatement预编译方式执行增删改操作,示例如下:

private void insertrecord() {
    String query ="INSERT INTO newpatient(patientId,patientName,patientAge,patientGender,patientAddress,patientMobile) VALUES (?,?,?,?,?,?)";
    // 使用try-with-resources语法自动关闭连接和语句对象,避免数据库文件被长期锁定
    try (Connection conn = getConnection();
         PreparedStatement pstmt = conn.prepareStatement(query)){
        pstmt.setInt(1, Integer.parseInt(newpatient_id.getText()));
        pstmt.setString(2, newpatient_name.getText());
        pstmt.setInt(3, Integer.parseInt(newpatient_age.getText()));
        pstmt.setString(4, selectedGender);
        pstmt.setString(5, newpatient_address.getText());
        pstmt.setLong(6, Long.parseLong(newpatient_mobile.getText()));
        
        pstmt.executeUpdate();
        System.out.println("Saved");
        Main.selected_patient_id = Integer.parseInt(newpatient_id.getText());
    } catch(Exception e) {
        System.out.println("Exception in Save");
        e.printStackTrace();
    }
}

现有代码中数据库连接、Statement执行完成后未主动关闭,会持续占用数据库文件句柄,运行时间长了会触发SQLite数据库锁,建议所有数据库操作都采用try-with-resources写法自动释放资源。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 11:36:20