2.4.2 使用PreparedStatement对象执行动态SQL

优质
小牛编辑
129浏览
2023-12-01

动态SQL实际上就是带参数的SQL。通过PreparedStatement对象可以执行动态的SQL。由于动态SQL没有参数名,只有参数索引,因此,PreparedStatement接口的getXxx方法和setXxx方法只能通过参数索引来确定参数。PreparedStatement对象和Statement对象的使用方法类似,所不同的是Connection对象使用prepareStatement方法创建PreparedStatement对象。在创建PreparedStatement对象时,必须使用prepareStatement方法指定一个动态SQL。下面是一个执行动态SQL语句的例子,代码如下:

public class DynamicSQL

{

    public static void main(String[] args) throws Exception

    {

        Class.forName("com.mysql.jdbc.Driver");

        Connection conn = DriverManager.getConnection(

                "jdbc:mysql://localhost/mydb?characterEncoding=UTF8",

                "root", "1234");

        String selectData = "SELECT name, author FROM t_books WHERE id = ?";

        //  获得PreparedStatement对象

        PreparedStatement pstmt = conn.prepareStatement(selectData);       

        pstmt.setInt(1, 1);                     // 赋参数值

        ResultSet rs = pstmt.executeQuery();

        //  输出返回的结果集

        while (rs.next())

        {

            System.out.println("书名:" + rs.getString("name"));

            System.out.println("作者:" + rs.getString("author"));

            System.out.println("---------------------");

        }

        pstmt.close();                          //  关闭PreparedStatement对象

        conn.close();   

    }

}

既然有了Statement对象,为什么JDBC又要引入PreparedStatement呢?其主要的原因有如下四点:

1. 提高代码可读性和可维护性

虽然使用PreparedStatement对象从表面上看要比使用Statement对象多好几行代码,但如果使用的SQL是通过字符串连接生成的,那么使用Statement对象 的代码的可读性就会变得很差,如下面代码如示:

使用Statement对象的代码

stmt.executeQuery("SELECT id, name, isbn, author,price FROM t_books WHERE id >" + id + " and name like '%" + subname + "%' and author = '" + author + "'");

使用PreparedStatement对象的代码

pstmt = conn.prepareStatement ("SELECT id, name, isbn, author,price FROM t_books WHERE id >? and name like ? and author = ?");

pstmt.setString(1,  id);

pstmt.setString(2,  subname);

pstmt.setString(3,  author);

pstmt.executeQuery();

从上面的代码可以看出,使用PreparedStatment对象的代码虽然多了几行,但显得更整洁。

2. 有助于提高性能

由于PreparedStatement对象在创建时就指定了动态的SQL,因此,这些SQL被DBMS编译后缓存了起来,等下次再执行相同的预编译语句时,就无需对其再编译,只需将参数值传入即可执行。由于动态SQL使用了“?”作为参数值占位符,因此,预编译语句的匹配几率要比Statement对象所使用的SQL语句大的多,所以,在多次调用同一条预编译语句时,PreparedStatement对象的性能要比Statement对象高得多。

3. 提高可复用性

动态SQL和存储过程十分类似,可以只写一次,然后只通过传递参数进行多次调用。这在执行需要不同条件的SQL时非常有用。

4. 提高安全性

在前面的章节讲过,execute、executeQuery和executeUpdate方法都可以执行多条SQL语句。那么这就存在一个安全隐患。如果使用Statement对象,并且SQL通过变量进行传递,就可能会受到SQL注入攻击,看下面的代码:

String author = "阿斯利森";

String selectData = "SELECT * FROM jdbcdemo.t_books where author = '" + author + "'";

stmt.executeQuery(selectData);

上面的代码并没有任何问题,但如果将author的值改为如下的字符串,就会在执行完SELECT语句后,将t_booksale删除。

"';drop table jdbcdemo.t_booksale;";

而如果使用PreparedStatement对象,就不会发生这样的事情。因此,在程序中应尽量使用PreparedStatement对象,并且在连接数据库时,除非必要,否则在连接字符串中不要加“allowMultiQueries=true”来打开执行多条SQL语句的功能。