PreparedStatement 和 Statement的区别

PreparedStatement 和 Statement的区别

      • [1. 代码可读性与可维护性](#1. 代码可读性与可维护性)
      • [2. 性能与预编译](#2. 性能与预编译)
      • [3. 安全性:防止 SQL 注入](#3. 安全性:防止 SQL 注入)
      • [4. 数据类型处理](#4. 数据类型处理)
      • [5. 适用范围](#5. 适用范围)
      • [6. 批量操作的支持](#6. 批量操作的支持)
      • 总结对比表

PreparedStatementStatement 都是 Java JDBC 中用来执行 SQL 语句的接口,但它们在性能、安全性、可读性 以及功能 上有着显著的区别。在实际开发中,通常推荐使用 PreparedStatement

以下是它们之间区别的详细展开说明:

1. 代码可读性与可维护性

  • Statement: 需要进行字符串拼接,尤其是当参数较多时,代码会变得冗长且难以阅读。

    java 复制代码
    // 需要手动拼接引号
    int id = 1;
    String name = "John";
    String sql = "SELECT * FROM users WHERE id = " + id + " AND name = '" + name + "'";
    Statement stmt = connection.createStatement();
    ResultSet rs = stmt.executeQuery(sql);
  • PreparedStatement: 使用占位符?,参数与 SQL 语句分离,结构清晰。

    java 复制代码
    String sql = "SELECT * FROM users WHERE id = ? AND name = ?";
    PreparedStatement pstmt = connection.prepareStatement(sql);
    pstmt.setInt(1, id);   // 参数索引从1开始
    pstmt.setString(2, name);
    ResultSet rs = pstmt.executeQuery(); // 这里不需要再传入sql

2. 性能与预编译

这是两者最核心的性能差异。

  • Statement: 每次执行 executeQuery()executeUpdate() 时,数据库系统都会对该 SQL 语句进行语法检查、语义检查、生成执行计划 。即使两次执行的 SQL 语句结构完全一样,只是参数不同(例如 WHERE id = 1WHERE id = 2),数据库也会把它们当作两个全新的语句来编译,不会重用之前的执行计划,导致效率较低。

  • PreparedStatement: 创建时会将 SQL 语句发送给数据库进行预编译。数据库一旦编译完成,就会将其缓存起来。之后无论传入什么参数,数据库都直接使用已编译好的执行计划,传入参数即可执行。

    • 优势: 在需要反复执行同一条 SQL 语句(如批量插入、频繁查询)时,可以节省每次编译的开销,显著提升性能。

3. 安全性:防止 SQL 注入

这是使用 PreparedStatement 最重要的理由。

  • Statement: 由于使用字符串拼接,攻击者可以利用输入数据修改原有的 SQL 逻辑。

    • 例子: 假设用户输入用户名作为参数,如果用户输入 ' OR '1'='1,拼接后的 SQL 可能变成:

      sql 复制代码
      SELECT * FROM users WHERE name = '' OR '1'='1' -- 恒成立,导致越权查询所有用户
  • PreparedStatement: 能够有效防止 SQL 注入。

    • 原因: PreparedStatement 在处理参数时,会对传入的参数进行转义 。它将传入的参数严格视为数据 ,而不是 SQL 关键字的一部分 。即使参数中包含 SQL 指令(如 OR 1=1),数据库也只会把它当作普通的字符串值来处理,而不会去执行其中的逻辑。

4. 数据类型处理

  • Statement: 所有参数都需要手动转换为字符串类型进行拼接,容易因格式问题引发错误,尤其是在处理日期、时间戳、二进制数据时。
  • PreparedStatement: 提供了丰富的 setXxx() 方法(如 setDate()setTimestamp()setBigDecimal()),能够更精确地将 Java 对象映射到对应的 SQL 数据类型,减少类型转换带来的错误。

5. 适用范围

  • Statement: 更适合执行不需要参数的 DDL(数据定义语言)语句 ,例如 CREATE TABLEALTER TABLE,或者执行一次性的、没有参数的 SQL。
  • PreparedStatement: 适用于需要传入参数的 DML(数据操作语言)语句SELECTINSERTUPDATEDELETE),尤其是需要批量执行的场景。

6. 批量操作的支持

  • PreparedStatement: 对于批量操作有很好的支持。可以使用 addBatch() 设置不同的参数,然后一次性通过 executeBatch() 提交给数据库执行,从而减少网络交互次数。

    java 复制代码
    // 预编译一次
    PreparedStatement pstmt = conn.prepareStatement("INSERT INTO users (id, name) VALUES (?, ?)");
    
    for (User user : userList) {
        pstmt.setInt(1, user.getId());
        pstmt.setString(2, user.getName());
        pstmt.addBatch(); // 添加到批次
    }
    
    pstmt.executeBatch(); // 一次性批量执行

总结对比表

特性 Statement PreparedStatement 结论
预编译 不预编译,每次执行都要编译 预编译,通常只编译一次 PreparedStatement 性能更好
SQL 注入 存在严重安全漏洞 自动防止 SQL 注入 PreparedStatement 更安全
可读性 参数拼接,混乱易错 参数占位符,结构清晰 PreparedStatement 更清晰
性能 低(尤其是循环执行时) 高(预编译+缓存执行计划) PreparedStatement 更高效
适用场景 表名/结构变化,DDL 数据操作,带参数的增删改查 各有所长,但 DML 多用后者

总的来说,在开发中,除非是执行结构变化的 DDL 语句,否则都应该优先使用 PreparedStatement

相关推荐
Micro麦可乐5 小时前
Spring Boot 实战:从零设计一个短链系统(含完整代码与数据库设计)
数据库·spring boot·后端·哈希算法·雪花算法·短链系统
码农阿豪5 小时前
从零到一:Spring Boot快速接入金仓数据库实战
数据库·spring boot·后端
鼎讯信通6 小时前
风电光缆运维提质增效:G-4000A 光缆故障追踪仪破解风场巡检难题
运维·网络·数据库
三十..6 小时前
MySQL 从入门到高可用架构实战精要
运维·数据库·mysql
cfm_29147 小时前
Redis五大基本数据结构底层了解
数据结构·数据库·redis
真实的菜7 小时前
Redis 从入门到精通(十二):典型业务场景实战 —— 排行榜、限流器、秒杀系统、Session 共享
数据库·redis·python
你想考研啊8 小时前
mysql数据库导出导入
数据库·mysql·oracle
十年编程老舅8 小时前
Linux DRM:底层逻辑与实践架构
数据库·mysql
The Sheep 20239 小时前
Vue复习
linux·服务器·数据库
云边有个稻草人9 小时前
深度解析:KingbaseES高可用架构落地原理与生产运维实战
数据库·读写分离·数据库运维·金仓数据库·国产数据库技术·数据备份恢复