执行SQL语句一共就三个接口Statement、PreparedStatement、CallableStatement。![](https://img.kancloud.cn/5a/38/5a388813a32d6ddc2ef448ff2e7cfd22_1119x134.jpg)
由于Statement不支持参数化命令,可能会导致SQL注入问题,即有人可以乱改动一部分代码,导致你的数据库发生意料之外的事。而使用PreparedSatement和CallableStatement要安全。
<br/>
**1. Statement查询**
Statement执行SQL语句不支持参数化命令,是纯字符串拼接,会导致SQL注入,及不安全。
```java
public class SafeTest {
private final String username = "lisi";
//像这样的字符串叫对等条件,也可以是"' or '2'='2";
private final String password = "' or '1'='1";
@Test
public void testStatement() throws SQLException {
String sql = "select username, password from admin where username='" + username + "' and password='" + password + "'";
System.out.println(sql);
//select username, password from admin where username='lisi' and password='' or '1'='1'
Connection conn = ConnUtils.getConnection3();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
//结果:账号与密码根本就不对,依然查出了所有数据
while (rs.next()) {
System.out.println(rs.getString("username") + "\t" + rs.getString("password"));
}
//zhangsan zhangsan123
//lisi lisi123
rs.close();
stmt.close();
conn.close();
}
}
```
**2. PreparedStatement查询**
PreparedStatement接口支持参数化命令,执行SQL语句安全。
```java
public class SafeTest {
private final String username = "lisi";
//像这样的字符串叫对等条件,也可以是"' or '2'='2";
private final String password = "' or '1'='1";
@Test
public void testPreparedStatement() throws SQLException {
String sql = "select username, password from admin where username=? and password=?";
Connection conn = ConnUtils.getConnection3();
//预编译
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, username);
pstmt.setString(2, password);
ResultSet rs = pstmt.executeQuery();
//结果:账号与密码不满足条件,所以一个都查不到,安全。
while (rs.next()) {
System.out.println(rs.getString("username") + "\t" + rs.getString("password"));
}
rs.close();
pstmt.close();
conn.close();
}
}
```
**3. CallableStatement查询**
CallableStatement接口用于执行对数据库存储过程的调用,该接口执行SQL语句更高效和安全。
<br/>
使用该接口的步骤如下:
(1)封装存储过程。
```sql
DELIMITER $$
CREATE
/*[DEFINER = { user | CURRENT_USER }]*/
PROCEDURE `jdbc_db`.`login`(IN user_ VARCHAR(50), IN pass_ VARCHAR(50))
# jdbc_db是数据库名、login是存储过程名、in为输入
# user_、pass_ 的数据类型必须与数据库中的admin表一致
# user_、pass_ 绝对不能与 admin表 上的列名一致,否则依旧是不安全的
/*LANGUAGE SQL
| [NOT] DETERMINISTIC
| { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
| SQL SECURITY { DEFINER | INVOKER }
| COMMENT 'string'*/
BEGIN
SELECT username,password FROM admin WHERE username=user_ AND `password`=pass_;
END$$
DELIMITER ;
```
(2)通过CallableStatement接口调用存储过程。
```java
public class SafeTest {
private final String username = "lisi";
//像这样的字符串叫对等条件,也可以是"' or '2'='2";
private final String password = "' or '1'='1";
@Test
public void testCallableStatement() throws SQLException {
//调用存储过程
String sql = "{call login(?,?)}";
Connection conn = ConnUtils.getConnection3();
// 预编译
CallableStatement cstmt = conn.prepareCall(sql);
cstmt.setString(1, username);
cstmt.setString(2, password);
ResultSet rs = cstmt.executeQuery();
//结果:账号与密码不满足条件,所以一个都查不到,安全。
while (rs.next()) {
System.out.println(rs.getString("username") + "\t" + rs.getString("password"));
}
rs.close();
cstmt.close();
conn.close();
}
}
```
- 网络通信
- 网络协议
- 端口和套接字
- TCP网络程序
- UDP网络程序
- 多线程聊天室
- 多线程
- 线程相关概念
- 线程实现方式
- 中断线程
- 线程生命周期
- 线程优先级
- 优先级规则
- 案例演示
- 线程同步机制
- 线程同步机制
- synchronized关键字
- ReentrantLock类
- Condition类
- 监视器概念
- volatile关键字
- final变量
- 死锁
- 线程局部变量
- 读/写锁
- 原子类
- 阻塞队列
- 工作规则
- 案例演示
- 常用阻塞队列
- 线程安全集合
- 高效的映射/集/队列
- 并发集视图
- 写数组的拷贝
- Arrays类的并行数组算法
- 同步包装器
- Callable与Future
- 执行器
- 线程池
- 预定执行
- 控制任务组
- Fork-Join框架
- 同步器
- 同步器
- 信号量
- CountDownLatch类
- CyclicBarrier类
- Exchanger类
- SynchronousQueue类
- 线程与Swing
- Swing与线程问题
- 两个原则
- Swing工作线程
- 单一线程规则
- 文件IO
- File类
- 文件输入输出
- ZIP压缩文件
- 集合
- 集合框架
- 集合接口
- 集合实现类
- 线程安全集合
- 集合算法
- 迭代器
- 集合排序
- JDBC
- JDBC是什么
- JDBC-ODBC桥
- JDBC驱动程序类型
- JDBC常用类与接口
- 数据库操作
- 连接数据库
- 增/删/改/查/预处理
- 事务
- 批处理
- commons-dbutils工具
- 安全问题
- Jedis
- 使用Jedis操作Redis数据库
- JSON转换
- 使用连接池
- 案例
- 单例破坏
- 单例定义
- 单例实现方式
- 懒汉式实现单例
- 饿汉式实现单例
- 单例破坏
- 类的单例破坏
- 枚举的单例破坏
- 克隆
- 克隆是什么
- 浅克隆
- 深克隆
- 注解
- 注解是什么
- 三大注解
- 内置注解
- 元注解
- 自定义注解
- NIO
- 相关概念
- BIO/NIO/AIO
- 多线程编程
- 线程同步
- 线程通信
- NIO
- NIO三大核心组件
- NIO网络编程
- NIO文件读写
- AIO
- Java8新特性
- Lambda表达式
- 方法引用
- 函数式接口
- 默认方法
- 什么是默认方法
- 默认方法语法格式
- 多个同名的默认方法问题
- 静态默认方法
- 默认方法实例
- Stream
- Stream是什么
- Stream示例
- Optional容器
- 新的日期时间API
- Base64
- SPI
- SPI是什么
- SPI与API的区别
- 常见场景
- 使用SPI需遵循的约定
- SPI使用步骤