JDBC mysql不支持PreparedStatement中的LIMIT占位符吗?

5

我使用了mysql-connector-java-5.1.38来操作Windows 10 64位上的mysql-community-5.7.10.0。

我试图绑定limit中的值以进行分页。

"SELECT * FROM employee LIMIT ?, ?"

然而结果显示:
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '?, ?' at line 1
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
    at java.lang.reflect.Constructor.newInstance(Unknown Source)
    at com.mysql.jdbc.Util.handleNewInstance(Util.java:404)
    at com.mysql.jdbc.Util.getInstance(Util.java:387)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:939)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3878)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3814)
    at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2478)
    at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2625)
    at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2547)
    at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2505)
    at com.mysql.jdbc.StatementImpl.executeQuery(StatementImpl.java:1370)
    at SqlTest.main(SqlTest.java:65)

然而,我尝试在 Navicat 中直接使用 SQL,但无法得到正确的答案:
INSERT INTO employee VALUES (1, 'Zara');
INSERT INTO employee VALUES (2, 'Zara');
INSERT INTO employee VALUES (3, 'Zara');
INSERT INTO employee VALUES (4, 'Zara');
SET @skip=1; SET @numrows=5;
PREPARE STMT FROM 'SELECT * FROM employee LIMIT ?, ?';
EXECUTE STMT USING @skip, @numrows;

这是我的全部代码:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class SqlTest {
  // JDBC driver name and database URL
  static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
  static final String DB_URL = "jdbc:mysql://localhost:3306/employee?useServerPrepStmts=false";
  // Database credentials
  static final String USER = "root";
  static final String PASS = "whaty123";

  static final int PAGESIZE = 10;

  public static void main(String[] args) {
    Connection conn = null;
    Statement stmt = null;
    PreparedStatement pStmt = null;

    // STEP 2: Register JDBC driver
    try {
      Class.forName("com.mysql.jdbc.Driver");
    } catch (ClassNotFoundException e) {
      e.printStackTrace();
    }

    // STEP 3: Open a connection
    System.out.println("Connecting to database...");
    try {
      conn = DriverManager.getConnection(DB_URL, USER, PASS);
    } catch (SQLException e) {
      e.printStackTrace();
    }

    String insertPreparedSql = "INSERT INTO employee " + "VALUES (?, 'Zara')";

    try {
      pStmt = conn.prepareStatement(insertPreparedSql);
    } catch (SQLException e) {
      e.printStackTrace();
    }

    for (int i = 0; i < 100; i++) {
      try {
        pStmt.setInt(1, i);
        pStmt.execute();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    }

    String selectLimitSql = "SELECT * FROM employee LIMIT ?, ?";
    // select with limit
    try {
      pStmt = conn.prepareStatement(selectLimitSql);
      pStmt.setFetchSize(PAGESIZE);
      pStmt.setMaxRows(PAGESIZE);
      pStmt.setFetchDirection(ResultSet.FETCH_FORWARD);
      int pageNo = 0;
      pStmt.setInt(1, pageNo * PAGESIZE);
      pStmt.setInt(2, PAGESIZE);
      ResultSet rs = pStmt.executeQuery(selectLimitSql);
      while (!rs.wasNull()) {
        while(rs.next()) {
          System.out.println("id: " + String.valueOf(rs.getInt(1)) + " name: " + rs.getString(2));
        }
        pageNo = pageNo + 1;
        pStmt.setInt(1, pageNo * PAGESIZE);
        pStmt.setInt(2, PAGESIZE);
        pStmt.executeQuery(selectLimitSql);
      }
      rs.close();
    } catch (SQLException e) {
      e.printStackTrace();
    }
  }

}

1
13.2.9 SELECT Syntax中提到,在预处理语句中,可以使用?占位符标记来指定LIMIT参数。@Abhik Chakraborty - chenatu
2个回答

7
您的问题不是语法或MySQL对LIMIT的支持,因为它已经被支持了。问题在于您执行PreparedStatement的方式。
在使用PreparedStatement时,不能使用executeQuery(String sql)方法,因为您已经为执行准备好了SQL字符串,不需要再次在executeQuery()方法中传递它。因此,请执行以下操作:
ResultSet rs = pStmt.executeQuery();

替代

ResultSet rs = pStmt.executeQuery(selectLimitSql);

再次通过selectLimitSql(如上一行所示),您将忽略以下内容:

pStmt.setInt(1, pageNo * PAGESIZE);
pStmt.setInt(2, PAGESIZE);

这就像执行包含'?, ?'占位符的原始纯sql,然后你会得到这个异常。


3
您不需要传递查询字符串。请按照以下方式操作:
 ResultSet rs = pStmt.executeQuery();

替换为
ResultSet rs = pStmt.executeQuery(selectLimitSql);

此外,由于分页已经在查询中通过limit实现,因此需要删除以下行。
pStmt.setFetchSize(PAGESIZE);
pStmt.setMaxRows(PAGESIZE);
pStmt.setFetchDirection(ResultSet.FETCH_FORWARD);

以下代码可行:

    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;

    public class SqlTest {
      // JDBC driver name and database URL
      static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
      static final String DB_URL = "jdbc:mysql://localhost:3306/company?useServerPrepStmts=false";
      // Database credentials
      static final String USER = "root";
      static final String PASS = "rohan";

      static final int PAGESIZE = 10;

      public static void main(String[] args) {
        Connection conn = null;
        Statement stmt = null;
        PreparedStatement pStmt = null;

        // STEP 2: Register JDBC driver
        try {
          Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
          e.printStackTrace();
        }

        // STEP 3: Open a connection
        System.out.println("Connecting to database...");
        try {
          conn = DriverManager.getConnection(DB_URL, USER, PASS);
        } catch (SQLException e) {
          e.printStackTrace();
        }

       String insertPreparedSql = "INSERT INTO employee " + "VALUES (?, 'Zara', 'Zara','Zara')";

        try {
          pStmt = conn.prepareStatement(insertPreparedSql);
        } catch (SQLException e) {
          e.printStackTrace();
        }

        for (int i = 0; i < 100; i++) {
          try {
            pStmt.setInt(1, i*10);
            pStmt.execute();
          } catch (SQLException e) {
            e.printStackTrace();
          }
        }

        String selectLimitSql = "SELECT * FROM employee limit ?, ?";
        // select with limit
        try {
          pStmt = conn.prepareStatement(selectLimitSql);
          int pageNo = 0;
          pStmt.setInt(1, pageNo * PAGESIZE);
          pStmt.setInt(2, PAGESIZE);
          ResultSet rs = pStmt.executeQuery();
          while (!rs.wasNull()) {
            while(rs.next()) {
              System.out.println("id: " + String.valueOf(rs.getInt(1)) + " name: " + rs.getString(2));
            }

            pageNo = pageNo + 1;
            pStmt.setInt(1, pageNo * PAGESIZE);
            pStmt.setInt(2, PAGESIZE);
            rs = pStmt.executeQuery();
          }
          rs.close();
        } catch (SQLException e) {
          e.printStackTrace();
        }
      }

    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接