64 lines
2.3 KiB
Markdown
64 lines
2.3 KiB
Markdown
### 有哪些 SQL 语句无法使用预编译的方式
|
||
|
||
**1. 动态的数据库对象名称**
|
||
|
||
预编译的参数只能用于替换 SQL 语句中的**值**(`VALUES`),而不能用于替换**表名**、**列名**、**排序字段**(`ORDER BY`)或**数据库名**
|
||
|
||
例如,你不能这样做:
|
||
|
||
```java
|
||
// 错误的预编译用法
|
||
String sql = "SELECT * FROM ? WHERE id = 1";
|
||
PreparedStatement pstmt = conn.prepareStatement(sql);
|
||
pstmt.setString(1, "users"); // 无法将表名作为参数传入
|
||
```
|
||
|
||
**2. 动态的 SQL 关键词或子句**
|
||
|
||
像 `SELECT`、`FROM`、`WHERE`、`GROUP BY`、`ORDER BY `等 SQL 关键词或整个子句都无法作为参数传入
|
||
|
||
例如,如果你想根据用户输入动态改变排序规则,你不能这样做:
|
||
|
||
```java
|
||
// 错误的预编译用法
|
||
String sql = "SELECT * FROM products ORDER BY ?";
|
||
PreparedStatement pstmt = conn.prepareStatement(sql);
|
||
pstmt.setString(1, "price DESC"); // 无法将排序规则作为参数传入
|
||
```
|
||
|
||
**3. 动态的 `IN` 子句中的值列表**
|
||
|
||
`IN` 子句中的值列表长度是可变的,预编译的占位符数量是固定的。因此,你不能直接将整个列表作为参数传入
|
||
|
||
例如,如果你想查询多个ID的用户,不能这样做:
|
||
|
||
```java
|
||
// 错误的预编译用法
|
||
String sql = "SELECT * FROM users WHERE id IN (?)";
|
||
PreparedStatement pstmt = conn.prepareStatement(sql);
|
||
pstmt.setString(1, "101, 102, 103"); // 字符串“101, 102, 103”会被当作一个值
|
||
```
|
||
|
||
**正确的做法:动态生成占位符**
|
||
|
||
对于这种情况,你需要在代码中根据用户输入的列表动态生成相应数量的占位符
|
||
|
||
```java
|
||
// 正确的做法
|
||
List<Integer> userIds = getUserIdsFromInput(); // 假设用户输入:101, 102, 103
|
||
StringBuilder sqlBuilder = new StringBuilder("SELECT * FROM users WHERE id IN (");
|
||
for (int i = 0; i < userIds.size(); i++) {
|
||
sqlBuilder.append("?");
|
||
if (i < userIds.size() - 1) {
|
||
sqlBuilder.append(", ");
|
||
}
|
||
}
|
||
sqlBuilder.append(")");
|
||
|
||
String sql = sqlBuilder.toString(); // 生成的SQL:SELECT * FROM users WHERE id IN (?, ?, ?)
|
||
PreparedStatement pstmt = conn.prepareStatement(sql);
|
||
for (int i = 0; i < userIds.size(); i++) {
|
||
pstmt.setInt(i + 1, userIds.get(i));
|
||
}
|
||
ResultSet rs = pstmt.executeQuery();
|
||
``` |