JDBC--使用PrepareStatement

1、PrepareStatement

PrepareStatement是 Statement的子接口,能够传入带占位符的 SQL语句,提供了补充占位符变量的方法。

使用 prepareStatement:java

1. 建立 PrepareStatement对象sql

  • String sql = "INSERT INTO Student VALUES(?,?,?,?,?,?)";
  • PrepareStatement ps = conn.prepareStatement(sql);

2. 调用 PrepareStatement的 setXxx(int index, Object val) 设置占位符的值,索引值从1开始。
3. 执行 SQL语句: executeQuery() 或 executeUpdate().。执行时不须要再传入 SQL 语句。数据库

2、使用带占位符的SQL语句

/**
     * PrepareStatement是 Statement的子接口,能够传入带占位符的 SQL语句
     * 提供了补充占位符变量的方法。
     *
     * 使用 prepareStatement:
     * 1. 建立 PrepareStatement对象
     *    String sql = "INSERT INTO Student VALUES(?,?,?,?,?,?)";
     *    PrepareStatement ps = conn.prepareStatement(sql);
     * 2. 调用 PrepareStatement的 setXxx(int index, Object val) 设置占位符的值
     *    索引值从1开始。
     * 3. 执行 SQL语句: executeQuery() 或 executeUpdate().
     *    执行时不须要再传入 SQL 语句。
     */
    @Test
    public void test8() {
        Connection conn = null;
        PreparedStatement preparedStatement = null;
         try {
             //获取数据库链接
             conn = getConnection2();
             //设置 SQL语句
             String sql = "INSERT INTO Student(Sno, Sname, Ssex, Sage, Sdept, S_entrance) " +
                          "VALUES(?,?,?,?,?,?)";
             //为占位符赋值
             preparedStatement = conn.prepareStatement(sql);
             preparedStatement.setString(1, "201415411");
             preparedStatement.setString(2, "李明");
             preparedStatement.setString(3, "男");
             preparedStatement.setInt(4, 20);
             preparedStatement.setString(5, "CS");
             preparedStatement.setDate(6, Date.valueOf("2014-09-09"));
             //执行 SQL语句
             preparedStatement.executeUpdate();
         } catch(Exception e) {
             e.printStackTrace();
         } finally {
             if(preparedStatement != null) {
                 try {
                     preparedStatement.close();
                 } catch(Exception e) {
                     e.printStackTrace();
                 }
             }
             if(conn != null) {
                 try {
                     conn.close();
                 } catch(Exception e) {
                     e.printStackTrace();
                 }
             }
         }
    }


    //getConnection2()方法
    public Connection getConnection2() throws Exception {
        //1.准备链接数据库的4个字符串
        //1.1 建立Properties对象
        Properties properties = new Properties();
        //1.2 获取jdbc.properties对应的输入流
        java.io.InputStream in = this.getClass().getClassLoader().getResourceAsStream("jdbc.properties");
        //1.3 加载文件
        properties.load(in);
        //1.4 给字符串赋值
        String driver = properties.getProperty("driver");
        String jdbcUrl = properties.getProperty("jdbcUrl");
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        //2.加载数据库驱动程序(对应的Driver实现类中有注册驱动的静态代码块)
        Class.forName(driver);
        //3.经过DriverManager的getConnection方法获取数据库链接
        return DriverManager.getConnection(jdbcUrl, user, password);
    }

3、SQL 注入攻击

SQL 注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的 SQL 语句段或命令,从而利用系统的 SQL 引擎完成恶意行为的作法。缓存

对于 Java 而言,要防范 SQL 注入,只要用 PreparedStatement 取代 Statement 就能够了。性能优化

以下程序,使用Statement,有SQL注入风险函数

/**
     * 用户表中有两列:用户username和密码password
     * 只有当用户和密码都正确时,登录成功,不然不成功
     * 
     */
    @Test
    public void test9() {
        //当用户名和密码设置以下,表中不存在这个用户名和密码,可是任然能够登陆成功
        String username = "a' OR password = ";
        String userpassword = " OR '1'='1";

        String sql = "SELECT * FROM Users WHERE username = '" +
                     username +"' AND " +
                     "password= '" + userpassword + "'";
        Connection conn = null;
        Statement statement = null;
        ResultSet resultSet = null;

        try {
            conn = getConnection2();
            statement = conn.createStatement();
            resultSet = statement.executeQuery(sql);
            if(resultSet.next()) {
                System.out.println("登录成功!");
            } else {
                System.out.println("用户名和密码不匹配或用户不存在");
            }
        } catch(Exception e) {
            e.printStackTrace();
        } finally {
            if(resultSet != null) {
                try {
                    resultSet.close();
                } catch(Exception e) {
                    e.printStackTrace();
                }
            }
            if(statement != null) {
                try {
                    statement.close();
                } catch(Exception e) {
                    e.printStackTrace();
                }
            }
            if(conn != null) {
                try {
                    conn.close();
                } catch(Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

当用户名和密码这样设置时:性能

String username = "a' OR password = ";
String userpassword = " OR '1'='1";学习

尽管表中没有这个用户名和密码,但依然能够登陆,此时SQL语句为:优化

SELECT * FROM Student WHERE username = 'a' OR password = ' AND Sno= ' OR '1'='1'this

如上,username的值为 a,password的值为 AND Sno=。整个WHERE条件是或条件判断,而 '1'='1' 是恒成立的,因此这条语句会执行。

使用PreparedStatement,将有效的解决注入问题

/**
     * 使用 PreparedStatement将有效的解决注入问题
     *
     */
    @Test
    public void test10() {
        String username = "a' OR Sno = ";
        String userpassword = " OR '1'='1";

        String sql = "SELECT * FROM Users WHERE username = ?" +
                " AND password= ?";

        Connection conn = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        try {
            conn = getConnection2();
            preparedStatement = conn.prepareStatement(sql);

            preparedStatement.setString(1, username);
            preparedStatement.setString(2, userpassword);
            resultSet = preparedStatement.executeQuery();
            if(resultSet.next()) {
                System.out.println("登录成功!");
            } else {
                System.out.println("用户名和密码不匹配或用户不存在");
            }
        } catch(Exception e) {
            e.printStackTrace();
        } finally {
            if(resultSet != null) {
                try {
                    resultSet.close();
                } catch(Exception e) {
                    e.printStackTrace();
                }
            }
            if(preparedStatement != null) {
                try {
                    preparedStatement.close();
                } catch(Exception e) {
                    e.printStackTrace();
                }
            }
            if(conn != null) {
                try {
                    conn.close();
                } catch(Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

4、PreparedStatement 能最大可能提升性能

  • DBServer会对预编译语句提供性能优化。由于预编译语句有可能被重复调用,因此语句在被DBServer的编译器编译后的执行代码被缓存下来,那么下次调用时只要是相同的预编译语句就不须要编译,只要将参数直接传入编译过的语句执行代码中就会获得执行。
  • 在statement语句中,即便是相同操做但由于数据内容不同,因此整个语句自己不能匹配,没有缓存语句的意义.事实是没有数据库会对普通语句编译后的执行代码缓存.这样每执行一次都要对传入的语句编译一次. 
  • (语法检查,语义检查,翻译成二进制命令,缓存)

 

 

JDBC学习笔记:

1. 获取数据库链接    http://my.oschina.net/daowuming/blog/704243

2. 经过Statement执行更新、查询操做    http://my.oschina.net/daowuming/blog/704384

3. 使用PrepareStatement    ----当前----

4. 使用ResultSetMetaData 对象处理结果集元数据    http://my.oschina.net/daowuming/blog/704487

5. 使用DatabaseMetaData获取数据库信息    http://my.oschina.net/daowuming/blog/704553

6. BLOB    http://my.oschina.net/daowuming/blog/704593

7. 处理事务与隔离级别    http://my.oschina.net/daowuming/blog/704611

8. 批量处理    http://my.oschina.net/daowuming/blog/704641

9. 数据库链接池    http://my.oschina.net/daowuming/blog/704700

10. 调用函数与存储过程    http://my.oschina.net/daowuming/blog/704813

相关文章
相关标签/搜索