• JDBC学习笔记(4)——PreparedStatement的使用


    PreparedStatement

    public interface PreparedStatement extends Statement;可以看到PreparedStatement是Statement的子接口,我们在执行查询或者更新数据表数据的时候,拼写SQL语句是一个很费力并且容易出错的事情,PreparedStatement可以简化这样的一个过程.

    PreParedStatement
    1).why?我们为什么要使用它
    使用Statement需要进行拼写SQl语句,辛苦并且容易出错,之前使用Statement的SQL语句的形式是这样的

    String sql = "insert into examstudent" + " values("
    + student.getFlowId() + "," + student.getType() + ",'"
    + student.getIdCard() + "','" + student.getExamCard() + "','"
    + student.getStudentName() + "','" + student.getLocation()
    + "'," + student.getGrade() + ")";

    使用PreparedStatement:是Statement的子接口,可以传入带占位符的SQL语句,提供了补充占位符变量的方法

    PreparedStatement ps=conn.preparedStatement(sql);

    可以看到将sql作为参数传入了,就不需要我们在费力拼写了。

    2)变成了这样的形式

    String sql="insert into examstudent values(?,?,?,?,?,?,?)";

    可以调用PreparedStatement的setXxx(int index,Object val)设置占位符的值,其中index的值从1开始

    执行SQl语句:excuteQuery()或者excuteUpdate()就可以完成查询或者数据的更新.【注意】:此时函数的参数位置不需要传入SQL语句,注意同使用Statement的update函数的差别

    具体代码实现:

    @Test
        public void testPreparedStatement() {
            Connection connection = null;
            PreparedStatement preparedStatement = null;
            try {
                // 连接数据库
                connection = JDBCTools.getConnection();
                // 使用占位符的SQl语句
                String sql = "insert into customers(name,email,birth)"
                        + "values(?,?,?)";
                // 使用preparedStatement的setXxx方法设置每一个位置上的值
                preparedStatement = connection.prepareStatement(sql);
                // 设置name字段
                preparedStatement.setString(1, "ATGUIGU");
                // 设置email字段
                preparedStatement.setString(2, "simale@163.com");
                // 设置birth字段
                preparedStatement.setDate(3,
                        new Date(new java.util.Date().getTime()));
                // 执行更新操作
                preparedStatement.executeUpdate();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                // 释放资源
                JDBCTools.release(null, preparedStatement, connection);
            }
        }

    使用PreparedStatement执行SQl(更新操作:插入、删除、更新,但不包括select查询操作),JDBCTools中的通用函数update更改成下面的形式:这里使用了可变参数,而不是使用数组

    public static void update(String sql,Object ...args){
            /**
             * 执行SQL语句,使用PreparedStatement
             */
            Connection connection=null;
            PreparedStatement preparedStatement=null;
            try {
                connection=JDBCTools.getConnection();
                preparedStatement=connection.prepareStatement(sql);
                for(int i=0;i<args.length;i++){
                    preparedStatement.setObject(i+1, args[i]);
                }
                preparedStatement.executeUpdate();
            } catch (Exception e) {
                e.printStackTrace();
            }finally{
                JDBCTools.release(null, preparedStatement, connection);
            }
        }

    使用PreparedStatement的好处:

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

    2).最大程度的提高性能:JDBC驱动的最佳化是基于使用的是什么功能. 选择PreparedStatement还是Statement取决于你要怎么使用它们. 对于只执行一次的SQL语句选择Statement是最好的. 相反, 如果SQL语句被多次执行选用PreparedStatement是最好的.PreparedStatement的第一次执行消耗是很高的. 它的性能体现在后面的重复执行(缓存的作用). 例如, 假设我使用Employee ID, 使用prepared的方式来执行一个针对Employee表的查询. JDBC驱动会发送一个网络请求到数据解析和优化这个查询. 而执行时会产生另一个网络请求. 在JDBC驱动中,减少网络通讯是最终的目的. 如果我的程序在运行期间只需要一次请求, 那么就使用Statement. 对于Statement, 同一个查询只会产生一次网络到数据库的通讯.当使用PreparedStatement池时, 如果一个查询很特殊, 并且不太会再次执行到, 那么可以使用Statement. 如果一个查询很少会被执行,但连接池中的Statement池可能被再次执行, 那么请使用PreparedStatement. 在不是Statement池的同样情况下, 请使用Statement.

    3).可以防止SQL注入

    SQL注入指的是通过构建特殊的输入作为参数传入Web应用程序,而这些输入大都是SQL语法里的一些组合,通过执行SQL语句进而执行攻击者所要的操作,其主要原因是程序没有细致地过滤用户输入的数据,致使非法数据侵入系统。

    比如我们新建一个数据表users,表中有两个字段username和password;

    我们在图形化界面SQLyog的sql语句的查询界面输入这样的查询语句:select * from users where username='a' or password='and password=' or '1'='1';

    执行该语句,会得到我们表中的数据:

    我们可以分析一下这条语句:where的后面,通过多个字段的组合作为查询过滤的条件。

    字段一:username='a'

    字段二:password='and password='

    字段三:'1'='1'

    因为用逻辑连接符OR来连接的三个字段,只要有一个为真就可以将查询工作完成.

    下面我们看下具体的代码实现:

        @Test
        public void testSQLinjection() {
            String username = "a' or password =";
            String password = " or '1'='1";
            String sql = "select * from users where username='" + username
                    + "' AND " + "password='" + password + "'";
            System.out.println(sql);
            Connection connection = null;
            Statement statement = null;
            ResultSet resultSet = null;
            try {
                connection = getConnection();
                statement = connection.createStatement();
                resultSet = statement.executeQuery(sql);
                if (resultSet.next()) {
                    System.out.println("登陆成功");
                } else {
                    System.out.println("不匹配");
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                JDBCTools.release(resultSet, statement, connection);
            }
        }

    运行结果:

    select * from users where username='a' or password =' AND password=' or '1'='1'
    登陆成功

    可以看到我们的SQl语句中都没有明确我们要查的字段的名,但是还是获取了查询的结果(SQL语句太能混了)

    于是,我们用了PreparedStatement就可以解决SQL注入的问题。

        @Test
        public void testSQLinjection2() {
            String username = "a' or password =";
            String password = " or '1'='1";
            String sql = "select * from users where username=?" + " and password=?";
            System.out.println(sql);
            Connection connection = null;
            PreparedStatement preparedStatement = null;
            ResultSet resultSet = null;
            try {
                connection = getConnection();
                preparedStatement = connection.prepareStatement(sql);
                preparedStatement.setString(1, username);
                preparedStatement.setString(2, password);
                resultSet = preparedStatement.executeQuery();
                if (resultSet.next()) {
                    System.out.println("登陆成功");
                } else {
                    System.out.println("不匹配");
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                JDBCTools.release(resultSet, preparedStatement, connection);
            }
        }

    执行结果:

    select * from users where username=? and password=?
    不匹配

    可以看到:再次使用伪装后的SQL语句已经不能获取我们数据表中的信息,我们这里在sql语句中使用了占位符。因此使用PreparedStatement可以结解决这里的SQL注入的问题。


    本文为博主原创文章,转载请注明出处:http://www.cnblogs.com/ysw-go/
    1、本博客的原创原创文章,都是本人平时学习所做的笔记,如有错误,欢迎指正。
    2、如有侵犯您的知识产权和版权问题,请通知本人,本人会即时做出处理文章。
    3、本博客的目的是知识交流所用,转载自其它博客或网站,作为自己的参考资料的,感谢这些文章的原创人员

  • 相关阅读:
    Leetcode Reverse Words in a String
    topcoder SRM 619 DIV2 GoodCompanyDivTwo
    topcoder SRM 618 DIV2 MovingRooksDiv2
    topcoder SRM 618 DIV2 WritingWords
    topcoder SRM 618 DIV2 LongWordsDiv2
    Zepto Code Rush 2014 A. Feed with Candy
    Zepto Code Rush 2014 B
    Codeforces Round #245 (Div. 2) B
    Codeforces Round #245 (Div. 2) A
    Codeforces Round #247 (Div. 2) B
  • 原文地址:https://www.cnblogs.com/ysw-go/p/5459330.html
Copyright © 2020-2023  润新知