使用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
相关产品推荐
相关产品推荐

