【数据库编程】4.Statement与PreparedStatement
1. Statement
1.1 基本介绍
- Statement用于执行静态SQL语句并返回其生成的结果的对象。
- Statement对象执行SQL语句存在SQL注入风险。
- SQL注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的SQL语句段或命令,恶意攻击数据库。
- 防范SQL注入,只要用PreparedStatement(Statement子接口)替代Statement即可。
1.2 SQL注入案例
创建一个admin表用于演示SQL注入,并插入一条数据。代码如下:
-- 创建一个用户表
CREATE TABLE admin (
`name` VARCHAR(32) NOT NULL UNIQUE,
`pwd` VARCHAR(32) NOT NULL DEFAULT ''
) CHARACTER SET = utf8;
-- 添加一条数据
INSERT INTO admin VALUES ('alan', '123');
编写一个Java程序,在控制台中输入用户名和密码模拟登陆,使用Statement执行拼接的SQL语句。如果用户名和密码正确就显示“登陆成功”,否则显示“登陆失败”。代码如下:
public static void testSQLInjection() throws Exception {
// 控制台输入用户名和密码
Scanner scanner = new Scanner(System.in);
System.out.print("请输入用户名:");
String name = scanner.nextLine();
System.out.print("请输入密码:");
String pwd = scanner.nextLine();
// 获取值
Properties properties = new Properties();
properties.load(new FileInputStream("src\\mysql.properties"));
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String dirver = properties.getProperty("dirver");
String url = properties.getProperty("url");
// 注册驱动
Class.forName(dirver);
// 得到连接和Statement
Connection connection = DriverManager.getConnection(url, user, password);
Statement statement = connection.createStatement();
// 组织SQL
String sql = "select count(name) from admin where name = '" + name + "' and pwd = '" + pwd + "'";
// 执行SQL获得结果集
ResultSet resultSet = statement.executeQuery(sql);
// 是否登陆成功
System.out.println(resultSet.next() ? "登陆成功" : "登陆失败");
// 关闭连接
resultSet.close();
statement.close();
connection.close();
}
当用户名输入【1' or】、密码输入【or '1'= '1】后,显示登陆成功。
此时的SQL语句拼接结果是
SELECT NAME FROM admin WHERE NAME = '1' OR' and pwd = 'OR '1'= '1'
2. PreparedStatement
2.1 基本介绍
- PreparedStatement执行的SQL语句中的参数用问号(?)来标识,调用PreparedStatement对象的setXxx()方法来设置这些参数。setXxx()方法有两个参数,第一个参数是要设置的SQL语句中的参数的索引(从1开始),第二个参数是设置SQL语句中的参数的值。
- 调用executeQuery(),执行查询,返回ResultSet对象。
- 调用executeUpdate(),执行更新,包括增、删、改。
2.2 预处理的优点
- 不再使用加号(+)拼接SQL语句,减少语法错误。
- 有效解决了SQL注入问题。
- 大大减少了编译次数,效率较高。
2.3 防范SQL注入案例
public static void testNoSQLInjection() throws Exception {
// 控制台输入用户名和密码
Scanner scanner = new Scanner(System.in);
System.out.print("请输入用户名:");
String name = scanner.nextLine();
System.out.print("请输入密码:");
String pwd = scanner.nextLine();
// 获取值
Properties properties = new Properties();
properties.load(new FileInputStream("src\\mysql.properties"));
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String dirver = properties.getProperty("dirver");
String url = properties.getProperty("url");
// 注册驱动
Class.forName(dirver);
// 得到连接和Statement
Connection connection = DriverManager.getConnection(url, user, password);
// 编写SQL
String sql = "select name from admin where name=? and pwd=?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
// 给占位符赋值
preparedStatement.setString(1, name);
preparedStatement.setString(2, pwd);
// 执行SQL获得结果集
ResultSet resultSet = preparedStatement.executeQuery();
// 是否登陆成功
System.out.println(resultSet.next() ? "登陆成功" : "登陆失败");
// 关闭连接
resultSet.close();
preparedStatement.close();
connection.close();
}
使用PreparedStatement后,用户名输入【1' or】、密码输入【or '1'= '1】后,显示“登陆失败”,解决了SQL注入。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)