PreparedStatement的使用规范
使用PreparedStatement替代Statement能有效防止SQL注入攻击,提高执行效率。创建PreparedStatement时应使用参数化查询:
java
String sql = "SELECT * FROM users WHERE username = ? AND password = ?";
PreparedStatement pstmt = connection.prepareStatement(sql);
pstmt.setString(1, username);
pstmt.setString(2, password);
ResultSet rs = pstmt.executeQuery();
参数索引从1开始,必须为每个参数设置合适的类型(setInt/setString等)。执行后必须关闭资源:
java
try (PreparedStatement pstmt = connection.prepareStatement(sql)) {
// 设置参数并执行
} catch (SQLException e) {
// 异常处理
}
批处理操作优化
对于批量数据操作,应使用addBatch()和executeBatch()方法:
java
PreparedStatement pstmt = connection.prepareStatement("INSERT INTO logs VALUES(?, ?)");
for (Log log : logList) {
pstmt.setString(1, log.getId());
pstmt.setTimestamp(2, log.getTime());
pstmt.addBatch();
}
int[] results = pstmt.executeBatch();
批处理大小建议控制在100-1000条之间,过大的批处理可能导致内存问题。
事务管理基本模式
JDBC事务应遵循ACID原则,默认自动提交需关闭:
java
connection.setAutoCommit(false);
try {
// 执行多个SQL操作
connection.commit();
} catch (SQLException e) {
connection.rollback();
} finally {
connection.setAutoCommit(true);
}
事务隔离级别应根据业务需求设置:
java
connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
事务超时设置
为防止长时间运行的事务,应设置超时时间:
java
int timeoutSeconds = 30;
connection.setNetworkTimeout(executor, timeoutSeconds * 1000);
对于分布式系统,需考虑XA事务或使用JTA实现。
连接池与事务关联
从连接池获取连接时,必须确保事务结束后重置连接状态:
java
try (Connection conn = dataSource.getConnection()) {
conn.setAutoCommit(false);
// 业务操作
conn.commit();
} catch (Exception e) {
// 异常时自动回滚
}
连接必须在使用后立即关闭,避免连接泄漏。
异常处理规范
捕获SQLException时应包含详细信息:
java
catch (SQLException e) {
logger.error("SQLState: " + e.getSQLState()
+ " ErrorCode: " + e.getErrorCode(), e);
throw new DataAccessException(e);
}
对可恢复错误(如死锁)应实现重试机制。
元数据缓存优化
频繁使用的PreparedStatement可缓存:
java
Map<String, PreparedStatement> statementCache = new LRUCache<>(100);
PreparedStatement getCachedStatement(String sql) {
return statementCache.computeIfAbsent(sql,
k -> connection.prepareStatement(k));
}
注意缓存大小需根据应用负载调整,避免内存溢出。