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

使用HikariCP连接SQLite执行查询时遇PreparedStatement异常求助

问题描述

使用HikariCP连接SQLite数据库时执行查询报错,异常信息如下:

Exception in thread "main" java.lang.RuntimeException: java.sql.SQLException: method cannot be called on a PreparedStatement
    at StatementUtility.wrapException(StatementUtility.java:110)
    at StatementUtility.executeUpdate(StatementUtility.java:101)
    at Database.createTables(Database.java:27)
    at Database.<init>(Database.java:23)
    at Main.main(Main.java:10)

Hikari连接配置:

public Client () {
    HikariConfig config = new HikariConfig();
    config.setPoolName("AuthMeSQLitePool");
    config.setDriverClassName("org.sqlite.JDBC");
    config.setConnectionTestQuery("SELECT 1");
    config.setJdbcUrl("jdbc:sqlite:I:/data.db");
    config.setMaxLifetime(60000);
    config.setIdleTimeout(45000);
    config.setMaximumPoolSize(50);
}

StatementUtility.java核心代码:

public <R> R executeQuery(String sql, ThrowingConsumer<PreparedStatement> cfg, ThrowingFunction<ResultSet, R, SQLException> op) {
    return wrapException(sql, s -> {
        cfg.accept(s);
        return op.apply(s.executeQuery());
    });
}

public int executeUpdate(String sql, ThrowingConsumer<PreparedStatement> cfg) {
    return wrapException(sql, s -> {
        cfg.accept(s);
        return s.executeUpdate();
    });
}

public <R> R executeQuery(String sql, ThrowingFunction<ResultSet, R, SQLException> op) {
    return wrapException(sql, s -> op.apply(s.executeQuery(sql)));
}

public int executeUpdate(String sql) {
    return wrapException(sql, s -> s.executeUpdate(sql));
}

private <T> T wrapException(String sql, @NotNull ThrowingFunction<PreparedStatement, T, SQLException> operation) {
    try(Connection connection = getConnection()){
        try (PreparedStatement statement = connection.prepareStatement(sql)) {
            return operation.apply(statement);
        }
    } catch (SQLException e) {
        throw new RuntimeException(e);
    }
}

已知原生查询语句执行正常,排除连接问题,寻求解决方法。

问题分析与解决

错误根源

问题出在无参数的executeQuery(String sql)和executeUpdate(String sql)方法中:

  • 工具类通过connection.prepareStatement(sql)创建了PreparedStatement对象,该对象的SQL语句在创建时已经固定
  • 但代码中调用了PreparedStatement.executeQuery(sql)和PreparedStatement.executeUpdate(sql)这两个带参数的方法,而PreparedStatement继承自Statement,这两个带参方法属于Statement的专属用法,PreparedStatement不支持(因为它的SQL已经预编译完成,不能再传入新的SQL语句)

修复方案

修改无参数的executeQuery和executeUpdate方法,直接调用PreparedStatement不带参数的executeQuery()和executeUpdate()方法即可,因为SQL语句已经在创建PreparedStatement时传入。

修改后的代码如下:

public <R> R executeQuery(String sql, ThrowingFunction<ResultSet, R, SQLException> op) {
    // 去掉executeQuery的sql参数,调用无参方法
    return wrapException(sql, s -> op.apply(s.executeQuery()));
}

public int executeUpdate(String sql) {
    // 去掉executeUpdate的sql参数,调用无参方法
    return wrapException(sql, s -> s.executeUpdate());
}

补充说明

SQLite的JDBC驱动对PreparedStatement的规范检查比较严格,而如果使用原生Statement(connection.createStatement())调用带参的execute方法是允许的,但你的工具类基于PreparedStatement实现,必须遵循其使用规则:预编译SQL语句后,只能通过设置参数(如果有)再调用无参的execute方法执行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 17:55:34