H2中通过PreparedStatement查询抛出异常:Prepared Statement不允许使用此方法;使用常规语句代替

Bas*_*que 3 sql select exception h2 executequery

使用 JDBC 4 访问Java 11 中的H2数据库时,通过预准备语句运行简单查询会失败。

\n\n

运行此行时:

\n\n
try ( ResultSet rs = pstmt.executeQuery( sql ) ; ) {\n
Run Code Online (Sandbox Code Playgroud)\n\n

\xe2\x80\xa6我收到此错误:

\n\n
\n

org.h2.jdbc.JdbcSQLException:准备好的语句不允许使用此方法;请改用常规语句。[90130-197]

\n
\n\n

我尝试使用H2 Error Analyzer,但没有帮助。

\n\n

这是一个完整的示例应用程序,位于单个 .java 文件中。您可以复制粘贴并自行运行。

\n\n
package com.basilbourque.example.work.basil.example.h2.pstmt_query;\n\nimport org.h2.jdbcx.JdbcDataSource;\n\nimport java.sql.*;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.UUID;\n\npublic class App {\n    public static void main ( String[] args ) {\n        App app = new App();\n        app.doIt();\n    }\n\n    private void doIt ( ) {\n\n        // Create database.\n        try {\n            Class.forName( "org.h2.Driver" );\n        } catch ( ClassNotFoundException e ) {\n            e.printStackTrace();\n        }\n        JdbcDataSource dataSource = new JdbcDataSource();\n        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.\n        dataSource.setUser( "scott" );\n        dataSource.setPassword( "tiger" );\n\n        // Create table.\n        try (\n                Connection conn = dataSource.getConnection() ;\n                Statement stmt = conn.createStatement() ;\n        ) {\n            String sql = "CREATE TABLE person_ ( \\n" +\n                    " pkey_ UUID NOT NULL DEFAULT RANDOM_UUID() PRIMARY KEY , \\n" +\n                    " name_ VARCHAR NOT NULL \\n" +\n                    ");";\n            System.out.println( sql );\n            stmt.execute( sql );\n        } catch ( SQLException e ) {\n            e.printStackTrace();\n        }\n\n        // Query table.\n        List < UUID > list = new ArrayList <>();\n        String sql = "SELECT * FROM person_ WHERE name_ = ? ;";\n        try (\n                Connection conn = dataSource.getConnection() ;\n                PreparedStatement pstmt = conn.prepareStatement( sql ) ;\n        ) {\n            String name = "Wendy Melvoin";\n            pstmt.setString( 1 , name );\n            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]\n                while ( rs.next() ) {\n                    UUID pkey = rs.getObject( "pkey_" , UUID.class );\n                    list.add( pkey );\n                }\n            }\n\n        } catch ( SQLException e ) {\n            e.printStackTrace();\n        }\n\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

报告异常:

\n\n
package com.basilbourque.example.work.basil.example.h2.pstmt_query;\n\nimport org.h2.jdbcx.JdbcDataSource;\n\nimport java.sql.*;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.UUID;\n\npublic class App {\n    public static void main ( String[] args ) {\n        App app = new App();\n        app.doIt();\n    }\n\n    private void doIt ( ) {\n\n        // Create database.\n        try {\n            Class.forName( "org.h2.Driver" );\n        } catch ( ClassNotFoundException e ) {\n            e.printStackTrace();\n        }\n        JdbcDataSource dataSource = new JdbcDataSource();\n        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.\n        dataSource.setUser( "scott" );\n        dataSource.setPassword( "tiger" );\n\n        // Create table.\n        try (\n                Connection conn = dataSource.getConnection() ;\n                Statement stmt = conn.createStatement() ;\n        ) {\n            String sql = "CREATE TABLE person_ ( \\n" +\n                    " pkey_ UUID NOT NULL DEFAULT RANDOM_UUID() PRIMARY KEY , \\n" +\n                    " name_ VARCHAR NOT NULL \\n" +\n                    ");";\n            System.out.println( sql );\n            stmt.execute( sql );\n        } catch ( SQLException e ) {\n            e.printStackTrace();\n        }\n\n        // Query table.\n        List < UUID > list = new ArrayList <>();\n        String sql = "SELECT * FROM person_ WHERE name_ = ? ;";\n        try (\n                Connection conn = dataSource.getConnection() ;\n                PreparedStatement pstmt = conn.prepareStatement( sql ) ;\n        ) {\n            String name = "Wendy Melvoin";\n            pstmt.setString( 1 , name );\n            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]\n                while ( rs.next() ) {\n                    UUID pkey = rs.getObject( "pkey_" , UUID.class );\n                    list.add( pkey );\n                }\n            }\n\n        } catch ( SQLException e ) {\n            e.printStackTrace();\n        }\n\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

Bas*_*que 7

传递 SQL 字符串两次

\n\n

在 上PreparedStatement,您永远不会将 SQL 字符串传递给executeQuery方法。你这样做是在一个毫无准备的情况下Statement,但不是PreparedStatement。请注意 JavaDoc for 如何PreparedStatement::executeQuery不带参数。

\n\n

所以你的行:

\n\n
try ( ResultSet rs = pstmt.executeQuery( sql ) ; ) {\n
Run Code Online (Sandbox Code Playgroud)\n\n

\xe2\x80\xa6 应该是:

\n\n
try ( ResultSet rs = pstmt.executeQuery() ; ) {\n
Run Code Online (Sandbox Code Playgroud)\n\n

sql当您准备语句时,您已经传递了该行上方指定的 SQL 字符串:

\n\n
PreparedStatement pstmt = conn.prepareStatement( sql ) ;\n
Run Code Online (Sandbox Code Playgroud)\n\n

由于PreparedStatementnamepstmt已经保存了您的 SQL 语句,因此无需传递到executeQuery.

\n\n

此错误可能是由于复制粘贴某些代码Statement以在其他使用PreparedStatement.

\n