A simple query fails when run through a prepared statement while using JDBC 4 to access an H2 database in Java 11.
When running this line:
try ( ResultSet rs = pstmt.executeQuery( sql ) ; ) {
…I get this error:
org.h2.jdbc.JdbcSQLException: This method is not allowed for a prepared statement; use a regular statement instead. [90130-197]
I tried using the H2 Error Analyzer, but no help there.
Here is a complete example app, in a single .java file. You can copy-paste and run yourself.
package com.basilbourque.example.work.basil.example.h2.pstmt_query; import org.h2.jdbcx.JdbcDataSource; import java.sql.*; import java.util.ArrayList; import java.util.List; import java.util.UUID; public class App { public static void main ( String[] args ) { App app = new App(); app.doIt(); } private void doIt ( ) { // Create database. try { Class.forName( "org.h2.Driver" ); } catch ( ClassNotFoundException e ) { e.printStackTrace(); } JdbcDataSource dataSource = new JdbcDataSource(); dataSource.setURL( "jdbc:h2:mem:pstmt_query_example_db;DB_CLOSE_DELAY=-1" ); // Set `DB_CLOSE_DELAY` to `-1` to keep in-memory database in existence after connection closes. dataSource.setUser( "scott" ); dataSource.setPassword( "tiger" ); // Create table. try ( Connection conn = dataSource.getConnection() ; Statement stmt = conn.createStatement() ; ) { String sql = "CREATE TABLE person_ ( n" + " pkey_ UUID NOT NULL DEFAULT RANDOM_UUID() PRIMARY KEY , n" + " name_ VARCHAR NOT NULL n" + ");"; System.out.println( sql ); stmt.execute( sql ); } catch ( SQLException e ) { e.printStackTrace(); } // Query table. List < UUID > list = new ArrayList <>(); String sql = "SELECT * FROM person_ WHERE name_ = ? ;"; try ( Connection conn = dataSource.getConnection() ; PreparedStatement pstmt = conn.prepareStatement( sql ) ; ) { String name = "Wendy Melvoin"; pstmt.setString( 1 , name ); try ( ResultSet rs = pstmt.executeQuery( sql ) ; ) { // org.h2.jdbc.JdbcSQLException: This method is not allowed for a prepared statement; use a regular statement instead. [90130-197] while ( rs.next() ) { UUID pkey = rs.getObject( "pkey_" , UUID.class ); list.add( pkey ); } } } catch ( SQLException e ) { e.printStackTrace(); } } }
Exception reported:
org.h2.jdbc.JdbcSQLException: This method is not allowed for a prepared statement; use a regular statement instead. [90130-197] at org.h2.message.DbException.getJdbcSQLException(DbException.java:357) at org.h2.message.DbException.get(DbException.java:179) at org.h2.message.DbException.get(DbException.java:155) at org.h2.message.DbException.get(DbException.java:144) at org.h2.jdbc.JdbcPreparedStatement.executeQuery(JdbcPreparedStatement.java:302) at com.basilbourque.example.work.basil.example.h2.pstmt_query.App.doIt(App.java:53) at com.basilbourque.example.work.basil.example.h2.pstmt_query.App.main(App.java:13)
Advertisement
Answer
Passing the SQL string twice
On a PreparedStatement
, you never pass the SQL string to executeQuery
method. You do so in an unprepared Statement
, but not PreparedStatement
. Notice how the JavaDoc for PreparedStatement::executeQuery
takes no argument.
So your line:
try ( ResultSet rs = pstmt.executeQuery( sql ) ; ) {
…should be:
try ( ResultSet rs = pstmt.executeQuery() ; ) {
You already passed the SQL string named sql
above that line, when you prepared the statement:
PreparedStatement pstmt = conn.prepareStatement( sql ) ;
Since the PreparedStatement
named pstmt
already holds your SQL statement(s), there is no need to pass into executeQuery
.
This mistake might have been the result of copy-pasting some code using Statement
for reuse in some other code using PreparedStatement
.