当前位置: 首页 > news >正文

PreparedStatement详解

PreparedStatement 详解


一、PreparedStatement 概述

PreparedStatement 是 JDBC 中用于执行预编译 SQL 语句的接口,它继承自 Statement 接口,提供了更安全、更高效的 SQL 执行方式。

主要特点:

  1. 预编译:SQL 语句被预编译并存储在 PreparedStatement 对象中
  2. 参数化查询:使用占位符(?)代替直接拼接SQL值
  3. 防止SQL注入:自动处理特殊字符,提高安全性
  4. 性能优势:可重复使用,减少数据库编译开销

二、创建 PreparedStatement

// 基本创建方式
String sql = "SELECT * FROM users WHERE id = ?";
PreparedStatement pstmt = connection.prepareStatement(sql);// 可指定结果集类型和并发模式
PreparedStatement pstmt = connection.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);// 可指定是否返回自动生成的主键
PreparedStatement pstmt = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);

三、参数设置方法

PreparedStatement 提供了一系列 setXxx() 方法来设置参数:

1. 基本数据类型

pstmt.setInt(int parameterIndex, int x);
pstmt.setLong(int parameterIndex, long x);
pstmt.setFloat(int parameterIndex, float x);
pstmt.setDouble(int parameterIndex, double x);
pstmt.setBoolean(int parameterIndex, boolean x);

2. 字符串和二进制数据

pstmt.setString(int parameterIndex, String x);
pstmt.setBytes(int parameterIndex, byte[] x);
pstmt.setBinaryStream(int parameterIndex, InputStream x);

3. 日期和时间

pstmt.setDate(int parameterIndex, Date x);
pstmt.setTime(int parameterIndex, Time x);
pstmt.setTimestamp(int parameterIndex, Timestamp x);// 使用Calendar指定时区
pstmt.setDate(int parameterIndex, Date x, Calendar cal);

4. 其他类型

pstmt.setObject(int parameterIndex, Object x);
pstmt.setNull(int parameterIndex, int sqlType);

参数索引说明:

  • 参数索引从1开始,不是0
  • 每个?占位符必须设置值
  • 可以多次设置同一参数的值(会覆盖前值)

四、执行 PreparedStatement

1. 执行查询(返回ResultSet)

ResultSet rs = pstmt.executeQuery();

2. 执行更新(返回影响行数)

int rows = pstmt.executeUpdate();

3. 执行任意SQL(可能返回多个结果)

boolean hasResultSet = pstmt.execute();
if (hasResultSet) {ResultSet rs = pstmt.getResultSet();
}

五、批处理操作

PreparedStatement 特别适合批处理操作:

String sql = "INSERT INTO products (id, name, price) VALUES (?, ?, ?)";
PreparedStatement pstmt = connection.prepareStatement(sql);for (Product product : productList) {pstmt.setInt(1, product.getId());pstmt.setString(2, product.getName());pstmt.setDouble(3, product.getPrice());pstmt.addBatch();  // 添加到批处理// 每100条执行一次,避免内存溢出if (i % 100 == 0) {pstmt.executeBatch();}
}// 执行剩余的批处理
int[] result = pstmt.executeBatch();// 清空批处理
pstmt.clearBatch();

六、获取自动生成的主键

String sql = "INSERT INTO users (name, age) VALUES (?, ?)";
PreparedStatement pstmt = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);pstmt.setString(1, "张三");
pstmt.setInt(2, 25);
pstmt.executeUpdate();// 获取生成的主键
ResultSet rs = pstmt.getGeneratedKeys();
if (rs.next()) {long id = rs.getLong(1);System.out.println("生成的主键ID: " + id);
}

七、PreparedStatement 高级特性

1. 流式处理大数据

// 设置大文本
Reader reader = new StringReader("很长的文本内容...");
pstmt.setCharacterStream(1, reader);// 设置二进制流
InputStream is = new FileInputStream("image.jpg");
pstmt.setBinaryStream(2, is);

2. 可滚动/可更新的ResultSet

PreparedStatement pstmt = connection.prepareStatement("SELECT * FROM employees WHERE dept = ?",ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE);pstmt.setString(1, "IT");
ResultSet rs = pstmt.executeQuery();// 更新结果集
rs.last();
rs.updateString("name", "New Name");
rs.updateRow();

3. 参数元数据

ParameterMetaData pmd = pstmt.getParameterMetaData();
int paramCount = pmd.getParameterCount();
for (int i = 1; i <= paramCount; i++) {String typeName = pmd.getParameterTypeName(i);System.out.println("参数" + i + "类型: " + typeName);
}

八、PreparedStatement 最佳实践

  1. 重用PreparedStatement:在循环外创建,循环内重复使用

    // 错误做法 - 每次循环都创建新的PreparedStatement
    for (User user : users) {PreparedStatement pstmt = conn.prepareStatement(...);// ...
    }// 正确做法 - 重用PreparedStatement
    PreparedStatement pstmt = conn.prepareStatement(...);
    for (User user : users) {pstmt.setString(1, user.getName());// ...pstmt.executeUpdate();
    }
    
  2. 总是关闭资源:使用try-with-resources

    try (PreparedStatement pstmt = connection.prepareStatement(sql)) {pstmt.setInt(1, 101);try (ResultSet rs = pstmt.executeQuery()) {// 处理结果}
    }
    
  3. 合理设置fetchSize:大数据量查询时提高性能

    PreparedStatement pstmt = connection.prepareStatement(sql);
    pstmt.setFetchSize(1000);  // 每次从数据库获取1000条
    
  4. 设置查询超时:避免长时间运行的查询

    pstmt.setQueryTimeout(30);  // 30秒超时
    
  5. 处理NULL值

    pstmt.setNull(1, Types.VARCHAR);  // 明确设置NULL类型
    

九、与Statement的比较

特性PreparedStatementStatement
SQL注入防护
性能高(预编译)低(每次编译)
参数设置参数化(?)字符串拼接
批处理高效效率较低
二进制数据支持好处理复杂
使用场景重复执行相似SQL一次性执行动态SQL

十、常见问题解决方案

1. 参数设置错误

问题:参数索引越界或类型不匹配

try {pstmt.setInt(1, 101);
} catch (SQLException e) {// 检查SQL中的?数量和设置是否匹配
}

2. 批处理部分失败

处理方式

try {int[] results = pstmt.executeBatch();
} catch (BatchUpdateException e) {int[] partialResults = e.getUpdateCounts();// 处理部分成功的情况
}

3. 大数据量处理

方案

// 设置适当的fetchSize
pstmt.setFetchSize(500);// 使用流式处理
pstmt.setFetchSize(Integer.MIN_VALUE);

PreparedStatement 是JDBC中最重要、最常用的接口之一,正确使用它可以显著提高应用程序的安全性、性能和可靠性。在实际开发中,应该优先使用PreparedStatement而不是Statement,特别是在处理用户输入或需要重复执行相似SQL时。

http://www.lqws.cn/news/579835.html

相关文章:

  • Vue3静态文档资源展示的实现和使用总结
  • 【CS创世SD NAND征文】SD NAND赋能新一代儿童智能玩具
  • js代码04
  • 生信分析之流式数据分析:Flowjo 软件核心功能全解析
  • 《微信生态裂变增长利器:推客小程序架构设计与商业落地》
  • 颠覆传统加密:微算法科技创新LSQb算法,提升量子图像处理速度
  • python | numpy小记(四):理解 NumPy 中的 `np.round`:银行家舍入策略
  • BUUCTF在线评测-练习场-WebCTF习题[MRCTF2020]你传你[特殊字符]呢1-flag获取、解析
  • 攻防世界-MISC-red_green
  • 障碍感知 | 基于3D激光雷达的三维膨胀栅格地图构建(附ROS C++仿真)
  • macos 使用 vllm 启动模型
  • 【数据分析】环境数据降维与聚类分析教程:从PCA到可视化
  • OpenCV CUDA模块设备层----计算向量的平方根函数sqrt
  • 【机器人】复现 HOV-SG 机器人导航 | 分层 开放词汇 | 3D 场景图
  • 极海G32R501双向数字电源解决方案 赋能AI服务器及电源应用创新
  • Android中Compose常用组件以及布局使用方法
  • 深入解析TCP:可靠传输的核心机制与实现逻辑
  • 首次使用“非英伟达”芯片!OpenAI租用谷歌TPU,降低推理计算成本
  • 成像光谱遥感技术中的AI革命:ChatGPT在遥感领域中的应用
  • (LeetCode 每日一题) 594. 最长和谐子序列 (哈希表)
  • redis相关内容以及安全知识
  • 开疆智能CCLinkIE转Canopen网关连接UV紫外灯配置案例
  • python包管理工具uv VS pip
  • iOS 接口频繁请求导致流量激增?抓包分析定位与修复全流程
  • 人工智能和云计算对金融未来的影响
  • 力扣 hot100 Day30
  • 键盘第一下无反应
  • Armbian 25.5.1 Noble Gnome 开启远程桌面功能
  • CMake中WIN32和CMAKE_HOST_WIN32的使用差异
  • Pytest pytest_runtest_makereport 钩子函数:测试失败信息收集与处理 —— Python 实践