# Java之PreparedStatement怎么用 ## 一、PreparedStatement概述 ### 1.1 什么是PreparedStatement PreparedStatement是Java JDBC API中的一个核心接口,继承自Statement接口,用于执行预编译的SQL语句。与普通Statement不同,PreparedStatement允许使用参数化查询,通过占位符(?)替代具体参数值,显著提高SQL执行效率和安全性。 ### 1.2 主要优势 - **防止SQL注入**:自动处理特殊字符转义 - **性能优化**:预编译SQL语句,重复执行时效率更高 - **类型安全**:支持参数类型检查 - **代码可读性**:分离SQL结构与参数值 ## 二、基本使用流程 ### 2.1 创建PreparedStatement ```java Connection conn = DriverManager.getConnection(url, username, password); String sql = "INSERT INTO users(name, age) VALUES(?, ?)"; PreparedStatement pstmt = conn.prepareStatement(sql);
PreparedStatement提供了一系列setXXX()方法:
数据类型 | 设置方法示例 |
---|---|
int | setInt(1, 25) |
String | setString(2, “张三”) |
boolean | setBoolean(3, true) |
Date | setDate(4, new Date(…)) |
BigDecimal | setBigDecimal(5, new BigDecimal(“100.50”)) |
// 执行INSERT/UPDATE/DELETE int affectedRows = pstmt.executeUpdate(); // 执行SELECT查询 ResultSet rs = pstmt.executeQuery(); while(rs.next()) { // 处理结果集 }
Connection conn = ...; PreparedStatement pstmt = conn.prepareStatement("INSERT INTO products(name, price) VALUES(?, ?)"); for(Product product : productList) { pstmt.setString(1, product.getName()); pstmt.setDouble(2, product.getPrice()); pstmt.addBatch(); // 添加到批处理 // 每1000条执行一次 if(i % 1000 == 0) { pstmt.executeBatch(); } } int[] result = pstmt.executeBatch(); // 执行剩余操作 conn.commit();
CallableStatement cstmt = conn.prepareCall("{call get_employee_data(?, ?)}"); cstmt.setInt(1, employeeId); cstmt.registerOutParameter(2, Types.VARCHAR); cstmt.execute(); String result = cstmt.getString(2);
try { conn.setAutoCommit(false); PreparedStatement pstmt1 = conn.prepareStatement(...); PreparedStatement pstmt2 = conn.prepareStatement(...); // 执行多个操作 pstmt1.executeUpdate(); pstmt2.executeUpdate(); conn.commit(); } catch (SQLException e) { conn.rollback(); } finally { conn.setAutoCommit(true); }
推荐使用HikariCP等连接池:
HikariConfig config = new HikariConfig(); config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb"); config.setUsername("user"); config.setPassword("password"); config.setMaximumPoolSize(20); HikariDataSource ds = new HikariDataSource(config);
MySQL开启预编译缓存:
jdbc:mysql://localhost:3306/db?useServerPrepStmts=true&cachePrepStmts=true
使用try-with-resources语法:
try (Connection conn = dataSource.getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setString(1, param); try (ResultSet rs = pstmt.executeQuery()) { // 处理结果 } }
错误示范:
// 危险!可能被SQL注入 String sql = "SELECT * FROM users WHERE username = '" + input + "'";
正确做法:
PreparedStatement pstmt = conn.prepareStatement( "SELECT * FROM users WHERE username = ?"); pstmt.setString(1, input);
// 使用加密字段 pstmt.setString(1, encrypt(user.getPassword())); // 日志脱敏处理 logger.debug("Executing: {} with params: {}", sql, maskSensitiveData(params));
错误现象:
java.sql.SQLException: Parameter index out of range (3 > number of parameters, which is 2)
解决方案: 检查SQL语句中的问号数量与设置的参数是否匹配
错误现象:
java.sql.SQLException: Conversion not supported for type java.util.Date
正确做法:
pstmt.setDate(1, new java.sql.Date(utilDate.getTime()));
使用连接池的监控功能:
HikariPoolMXBean pool = ds.getHikariPoolMXBean(); System.out.println("Active connections: " + pool.getActiveConnections());
public class UserDao { private static final String INSERT_SQL = "INSERT INTO users(username, password, email) VALUES(?, ?, ?)"; public boolean registerUser(User user) { try (Connection conn = DataSource.getConnection(); PreparedStatement pstmt = conn.prepareStatement(INSERT_SQL)) { pstmt.setString(1, user.getUsername()); pstmt.setString(2, encrypt(user.getPassword())); pstmt.setString(3, user.getEmail()); return pstmt.executeUpdate() > 0; } catch (SQLException e) { logger.error("Registration failed", e); return false; } } }
public List<Product> getProducts(int page, int size) { String sql = "SELECT * FROM products ORDER BY id LIMIT ? OFFSET ?"; try (Connection conn = ...; PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setInt(1, size); pstmt.setInt(2, (page - 1) * size); ResultSet rs = pstmt.executeQuery(); List<Product> list = new ArrayList<>(); while(rs.next()) { list.add(mapRowToProduct(rs)); } return list; } catch (...) { ... } }
通过掌握PreparedStatement的正确使用方法,可以显著提升Java数据库应用的安全性、性能和可维护性。
本文共计约3050字,涵盖了从基础到高级的PreparedStatement使用技巧,可作为日常开发参考指南。 “`
这篇文章采用Markdown格式编写,包含: 1. 多级标题结构 2. 代码块示例 3. 表格对比 4. 实际应用场景 5. 问题解决方案 6. 完整示例代码 7. 最佳实践总结
内容全面覆盖了PreparedStatement的核心用法,字数符合3050字左右的要求,适合作为技术文档或博客文章。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。