为何Java中PreparedStatement SQL查询不生效,仅硬编码字符串可用?
核心问题
你完全误用了PreparedStatement的执行逻辑,错误点非常明确:
你在createPreparedStatementQuery方法中将构造好的PreparedStatement对象通过toString()转成字符串返回,再传给普通Statement的executeUpdate方法执行。但MySQL驱动的PreparedStatement的toString()返回值会自带类名前缀com.mysql.cj.jdbc.ClientPreparedStatement: ,你打印的时候看起来和纯SQL一致,但实际传给数据库的SQL开头带了这段不属于SQL语法的内容,因此直接触发语法错误。
正确用法说明
PreparedStatement本身就自带执行方法,不需要转成字符串丢给普通Statement执行,正确流程如下:
- 定义带占位符的SQL模板
- 预编译得到
PreparedStatement对象,同时指定返回自动生成的主键 - 给所有占位符赋值
- 直接调用
PreparedStatement自身的无参executeUpdate()方法执行
修正后代码示例
public Customer createCustomer(Customer customer) { // 省略其他代码 String query = "INSERT INTO customers(Customer_Name, Address, Postal_Code, Phone, Created_By, Last_Updated_By, Division_ID) " + "VALUES (?, ?, ?, ?, ?, ?, (SELECT Division_ID FROM first_level_divisions WHERE division = ?));"; try ( // 构造PreparedStatement时就指定需要返回生成的主键 PreparedStatement statement = ConnectionManager.getConnection().prepareStatement(query, Statement.RETURN_GENERATED_KEYS) ) { // 给占位符赋值 statement.setString(1, customer.getName()); statement.setString(2, customer.getAddress()); statement.setString(3, customer.getPostalCode()); statement.setString(4, customer.getPhoneNumber()); statement.setString(5, customer.getCreatedBy()); statement.setString(6, customer.getLastUpdatedBy()); statement.setString(7, customer.getDivision()); // 直接执行,不需要传入SQL参数 int result = statement.executeUpdate(); // 后续可通过statement.getGeneratedKeys()拿到自增主键填充customer对象 } catch (SQLException e) { e.printStackTrace(); } // 省略其他返回逻辑 }
额外注意
字符串拼接SQL的写法存在SQL注入风险,PreparedStatement的预编译机制本来就是为了解决这个问题,你之前的用法完全浪费了它的安全优势。
内容的提问来源于stack exchange,提问作者WHOATEMYNOODLES
相关产品推荐
相关产品推荐

