PreparedStatement预编译原理及基础使用

发布时间 2023-12-11 09:17:18作者: べ微熏の斜陽べ

PreparedStatement介绍

PreparedStatement 是 JDBC 中的一个接口,用于执行预编译的 SQL 语句。与普通的 Statement 不同,PreparedStatement 的 SQL 语句在执行之前已经经过编译,因此更高效且安全,同时可以防止 SQL 注入攻击。PreparedStatement 通常用于执行多次相似的 SQL 查询或更新,只需编译一次,多次执行。

PreparedStatement 对象所代表的 SQL 语句中的参数用问号(?)来表示,调用 PreparedStatement 对象的 setXxx() 方法来设置这些参数. setXxx() 方法有两个参数,第一个参数是要设置的 SQL 语句中的参数的索引(从 1 开始),第二个是设置的 SQL 语句中的参数的值

为什么预编译(PrepareStatement)可以防止sql注入

原理是采用了预编译的方法,先将SQL语句中可被客户端控制的参数集进行编译,生成对应的临时变量集,再使用对应的设置方法,为临时变量集里面的元素进行赋值,赋值函数setString(),会对传入的参数进行强制类型检查和安全检查,所以就避免了SQL注入的产生。

什么是SQL注入,怎么防止SQL注入?

1、所谓SQL注入,就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。具体来说,它是利用现有应用程序,将(恶意)的SQL命令注入到后台数据库引擎执行的能力,它可以通过在Web表单中输入(恶意)SQL语句得到一个存在安全漏洞的网站上的数据库,而不是按照设计者意图去执行SQL语句。

2、怎么防止SQL注入,使用存储过程来执行所有的查询;检查用户输入的合法性;将用户的登录名、密码等数据加密保存。

PreparedStatement的基础使用

创建 PreparedStatement

要创建一个 PreparedStatement 对象,首先需要获得一个 Connection 对象,然后使用 prepareStatement 方法传入 SQL 语句。以下是一个示例:

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class PreparedStatementDemo {
    public static void main(String[] args) {
        // 获取数据库连接
        Connection connection = getConnection();

        try {
            // SQL 查询语句,使用 ? 作为占位符
            String sql = "SELECT * FROM users WHERE username = ?";

            // 创建 PreparedStatement 对象
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    
    // 获取数据库连接的示例方法
    private static Connection getConnection() {
        // 实现获取数据库连接的逻辑,这里省略具体代码
        return null;
    }
}

在上述示例中,我们创建了一个 PreparedStatement 对象,其中 SQL 查询语句中使用了 ? 作为占位符,后面可以使用 setXXX 方法为这些占位符设置具体的值。

设置参数

PreparedStatement 允许我们为 SQL 语句中的占位符设置参数值。有多种 setXXX 方法可用于不同数据类型的参数设置,例如 setIntsetStringsetDouble 等。以下是一个设置参数的示例:

try {
    String sql = "INSERT INTO users (username, age) VALUES (?, ?)";
    PreparedStatement preparedStatement = connection.prepareStatement(sql);

    // 设置第一个参数为字符串类型
    preparedStatement.setString(1, "Alice");

    // 设置第二个参数为整数类型
    preparedStatement.setInt(2, 30);

    // 执行 SQL 语句
    preparedStatement.executeUpdate();
} catch (SQLException e) {
    e.printStackTrace();
}

在上述示例中,我们使用 setStringsetInt 方法分别为 SQL 语句中的两个占位符设置了具体的参数值。

执行查询

要执行查询操作,可以使用 executeQuery 方法,该方法返回一个 ResultSet 对象,用于存储查询结果。以下是一个示例:

try {
    String sql = "SELECT * FROM users WHERE age > ?";
    PreparedStatement preparedStatement = connection.prepareStatement(sql);

    // 设置参数
    preparedStatement.setInt(1, 25);

    // 执行查询
    ResultSet resultSet = preparedStatement.executeQuery();

    // 处理查询结果
    while (resultSet.next()) {
        String username = resultSet.getString("username");
        int age = resultSet.getInt("age");
        System.out.println("Username: " + username + ", Age: " + age);
    }
} catch (SQLException e) {
    e.printStackTrace();
}

在上述示例中,我们执行了一个带有占位符的查询操作,并通过 setInt 方法设置了占位符的参数值,然后使用 executeQuery 方法执行查询,最后遍历 ResultSet 获取查询结果。

执行更新

要执行更新操作(如插入、更新、删除),可以使用 executeUpdate 方法。以下是一个示例:

try {
    String sql = "UPDATE users SET age = ? WHERE username = ?";
    PreparedStatement preparedStatement = connection.prepareStatement(sql);

    // 设置参数
    preparedStatement.setInt(1, 28);
    preparedStatement.setString(2, "Alice");

    // 执行更新
    int rowCount = preparedStatement.executeUpdate();

    // 输出更新的行数
    System.out.println("Updated " + rowCount + " rows.");
} catch (SQLException e) {
    e.printStackTrace();
}

在上述示例中,我们执行了一个带有占位符的更新操作,通过 setIntsetString 方法设置了占位符的参数值,然后使用 executeUpdate 方法执行更新操作,并输出更新的行数。

执行批处理

PreparedStatement 还支持批处理,即一次性执行多个 SQL 语句。这对于需要频繁执行相似 SQL 语句的情况非常有用,可以提高性能。以下是一个批处理的示例:

try {
    String insertSql = "INSERT INTO users (username, age) VALUES (?, ?)";
    String updateSql = "UPDATE users SET age = ? WHERE username = ?";

    // 创建 PreparedStatement 对象
    PreparedStatement insertStatement = connection.prepareStatement(insertSql);
    PreparedStatement updateStatement = connection.prepareStatement(updateSql);

    // 设置参数并添加到批处理中
    for (int i = 1; i <= 3; i++) {
        insertStatement.setString(1, "User" + i);
        insertStatement.setInt(2, 25 + i);
        insertStatement.addBatch();

        updateStatement.setInt(1, 30 + i);
        updateStatement.setString(2, "User" + i);
        updateStatement.addBatch();
    }

    // 执行批处理
    int[] insertResult = insertStatement.executeBatch();
    int[] updateResult = updateStatement.executeBatch();

    // 输出批处理结果
    System.out.println("Inserted rows: " + Arrays.toString(insertResult));
    System.out.println("Updated rows: " + Arrays.toString(updateResult));
} catch (SQLException e) {
    e.printStackTrace();
}

在上述示例中,我们创建了两个 PreparedStatement 对象,并使用 addBatch 方法将多个 SQL 语句添加到批处理中,然后使用 executeBatch 方法一次性执行批处理中的所有 SQL 语句。

关闭 PreparedStatement

在使用完 PreparedStatement 后,应该及时关闭它以释放资源。可以使用 close 方法来关闭 PreparedStatement。以下是一个关闭 PreparedStatement 的示例:

try {
    // 创建 PreparedStatement 对象
    PreparedStatement preparedStatement = connection.prepareStatement(sql);

    // 设置参数...
    
    // 执行操作...
    
    // 关闭 PreparedStatement
    preparedStatement.close();
} catch (SQLException e) {
    e.printStackTrace();
}

总结

PreparedStatement 是 JDBC 中用于执行预编译 SQL 语句的重要接口,它具有高效性、安全性和可维护性的优势。在实际应用中,使用 PreparedStatement 能够有效地防止 SQL 注入攻击,并提高数据库操作的性能。通过本文的介绍,您应该对PreparedStatement 的基本概念和使用方法有了更清晰的理解。在编写数据库相关的 Java 应用程序时,不妨考虑使用 PreparedStatement 来执行 SQL 操作。