Skip to content
Advertisement

Query through PreparedStatement in H2 throws exception: This method is not allowed for a prepared statement; use a regular statement instead

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.

User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement