• JDBC | 第三章: JDBC之SQL预编译与防注入CRUD操作


    在java的JDBC中预编译执行SQL主要是用到PreparedStatement对象SQL 语句被预编译并存储在 PreparedStatement 对象中。然后可以使用此对象多次高效地执行该语句可以通过调用 Connection 对象的 preparedStatement() 方法获取 PreparedStatement 对象 PreparedStatement 对象所执行的 SQL 语句中,参数用问号(?)来表示,调用 PreparedStatement 对象的 setXXX() 方法来设置这些参数. setXXX() 方法有两个参数,第一个参数是要设置的 SQL 语句中的参数的索引(从 1 开始),第二个是设置的 SQL 语句中的参数的值,注意用setXXX方式设置时,需要与数据库中的字段类型对应,例如mysql中字段为varchar,就需要使用setString方法,如果为Date类型,就需要使用setDate方法来设置具体sql的参数。简单来说就是,预编译的SQL语句不是有具体数值的语句,而是用(?)来代替具体数据,然后在执行的时候再调用setXX()方法把具体的数据传入。同时,这个语句只在第一次执行的时候编译一次,然后保存在缓存中。之后执行时,只需从缓存中抽取编译过了的代码以及新传进来的具体数据,即可获得完整的sql命令。这样一来就省下了后面每次执行时语句的编译时间。

    使用预编译分4步走

    1:定义预编译的sql语句,其中待填入的参数用 ? 占位。注意,?无关类型,不需要加分号之类。其具体数据类型在下面setXX()时决定。

    2:创建预编译Statement,并把sql语句传入。此时sql语句已与此preparedStatement绑定。所以第4步执行语句时无需再把sql语句作为参数传入execute()

    3:填入具体参数。通过setXX(问号下标,数值)来为sql语句填入具体数据。注意:问号下标从1开始,setXX与数值类型有关,字符串就是setString(index,str).

    4: 注意,前面创建preparedstatement时已经把sql语句传入了,此时执行不需再把sql语句传入,这是与一般statement执行sql语句所不同之处。

    添加操作

      public int ins() {
            Connection connection = null;
            PreparedStatement preparedStatement = null;
            //这里参数用?占位符表示
            String sql = "insert into student (name, age, addr, hobby) values (?,?,?,?)";
            try {
                //获取数据连接
                connection = basicUse.getConnection();
    
                //获取发送sql指令执行sql进行预编译
                preparedStatement = connection.prepareStatement(sql);
    
                //设置sql语句参数索引从1开始
                preparedStatement.setObject(1, "Tom");
                preparedStatement.setObject(2, 24);
                preparedStatement.setObject(3, "上海");
                preparedStatement.setObject(4, "篮球");
    
                System.out.println("执行sql" + sql);
                //执行成功返回1
                int success = preparedStatement.executeUpdate(); //注意这里不需要在放入sql
                return success;
            } catch (Exception e) {
                e.printStackTrace();
                return 0;
            } finally {
                //执行完数据库操作后记得关闭数据库连接资源
                try {
    
                    preparedStatement.close();
                    connection.close();
    
                } catch (SQLException e) {
                    e.printStackTrace();
                }
    
            }
    
        }
    

    查询操作

     public void select() {
            Connection connection = null;
             PreparedStatement preparedStatement = null;
            ResultSet rs = null;
            String sql = "select * from user limit 0,1";
            try {
                //获取数据连接
                connection = basicUse.getConnection();
                //获取发送sql指令执行sql对象
                preparedStatement = connection.prepareStatement(sql);
                //返回查询结果集用于保存数据库查询内容
                rs = preparedStatement.executeQuery();
                //遍历结果集拿到数据
                while (rs.next()) {
                    System.out.println("id" + "\t" + rs.getString("id"));
                    System.out.println("name" + "\t" + rs.getString("name"));
                    System.out.println("age" + "\t" + rs.getString("age"));
                    System.out.println("email" + "\t" + rs.getString("email"));
                    System.out.println("manager_id" + "\t" + rs.getString("manager_id"));
                    System.out.println("create_time" + "\t" + rs.getString("create_time"));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                //执行完数据库操作后记得关闭数据库连接资源
                try{
                    rs.close();
                    preparedStatement.close();
                    connection.close();
    
                }catch (SQLException e){
                    e.printStackTrace();
                }
    
            }
        }
    

    更新操作

     public int update() {
            Connection connection = null;
            PreparedStatement preparedStatement = null;
            //这里参数用?占位符表示
            String sql = "update student set hobby=? where id = 11";
            try {
                //获取数据连接
                connection = basicUse.getConnection();
    
                //获取发送sql指令执行sql进行预编译
                preparedStatement = connection.prepareStatement(sql);
    
                //设置sql语句参数索引从1开始
                preparedStatement.setObject(1, "足球");
    
    
                System.out.println("执行sql" + sql);
                //执行成功返回1
                int success = preparedStatement.executeUpdate(); //注意这里不需要在放入sql
                return success;
            } catch (Exception e) {
                e.printStackTrace();
                return 0;
            } finally {
                //执行完数据库操作后记得关闭数据库连接资源
                try {
    
                    preparedStatement.close();
                    connection.close();
    
                } catch (SQLException e) {
                    e.printStackTrace();
                }
    
            }
    
        }
    

    删除操作

        public int del() {
            Connection connection = null;
            PreparedStatement preparedStatement = null;
            //这里参数用?占位符表示
            String sql = "delete from student where id=?";
            try {
                //获取数据连接
                connection = basicUse.getConnection();
    
                //获取发送sql指令执行sql进行预编译
                preparedStatement = connection.prepareStatement(sql);
    
                //设置sql语句参数索引从1开始
                preparedStatement.setObject(1, 11);
    
    
                System.out.println("执行sql" + sql);
                //执行成功返回1
                int success = preparedStatement.executeUpdate(); //注意这里不需要在放入sql
                return success;
            } catch (Exception e) {
                e.printStackTrace();
                return 0;
            } finally {
                //执行完数据库操作后记得关闭数据库连接资源
                try {
    
                    preparedStatement.close();
                    connection.close();
    
                } catch (SQLException e) {
                    e.printStackTrace();
                }
    
            }
    
        }
    

    批量操作

     public int[] updateBatch() {
            Connection connection = null;
            PreparedStatement preparedStatement = null;
            //这里参数用?占位符表示
            String sql = "update student set hobby=? where id = 11";
            try {
                //获取数据连接
                connection = basicUse.getConnection();
    
                //获取发送sql指令执行sql进行预编译
                preparedStatement = connection.prepareStatement(sql);
    
                //批量执行sql
                for (int i = 12; i <= 16; i++) {
                    //设置sql语句参数索引从1开始
                    preparedStatement.setObject(1, i);
                    preparedStatement.addBatch();
                }
                System.out.println("执行sql" + sql);
                //执行成功返回更新计数的数组
                int[] success = preparedStatement.executeBatch(); //注意这里不需要在放入sql
                return success;
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            } finally {
                //执行完数据库操作后记得关闭数据库连接资源
                try {
    
                    preparedStatement.close();
                    connection.close();
    
                } catch (SQLException e) {
                    e.printStackTrace();
                }
    
            }
    
        }
    

    使用预编译的好处

    1:PreparedStatement比 Statement 更快
    使用 PreparedStatement 最重要的一点好处是它拥有更佳的性能优势,SQL语句会预编译在数据库系统中。执行计划同样会被缓存起来,它允许数据库做参数化查询。使用预处理语句比普通的查询更快,因为它做的工作更少(数据库对SQL语句的分析,编译,优化已经在第一次查询前完成了)。

    2:PreparedStatement可以防止SQL注入式攻击
    SQL 注入攻击:SQL 注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的 SQL 语句段或命令,从而利用系统的 SQL 引擎完成恶意行为的做法。

    比如:某个网站的登录验证SQL查询代码为:
    strSQL = "SELECT * FROM users WHERE name = '" + userName + "' and pw = '"+ passWord +"';"
    恶意填入:
    userName = "1' OR '1'='1"; passWord = "1' OR '1'='1";
    那么最终SQL语句变成了:
    strSQL = "SELECT * FROM users WHERE name = '1' OR '1'='1' and pw = '1' OR '1'='1';"
    因为WHERE条件恒为真,这就相当于执行:
    strSQL = "SELECT * FROM users;"
    因此可以达到无账号密码亦可登录网站。
    如果恶意用户要是更坏一点,SQL语句变成:
    strSQL = "SELECT * FROM users WHERE name = 'any_value' and pw = ''; DROP TABLE users"
    这样一来,虽然没有登录,但是数据表都被删除了。

    使用PreparedStatement的参数化的查询可以阻止大部分的SQL注入。在使用参数化查询的情况下,数据库系统不会将参数的内容视为SQL指令的一部分来处理,而是在数据库完成SQL指令的编译后,才套用参数运行,因此就算参数中含有破坏性的指令,也不会被数据库所运行。因为对于参数化查询来说,查询SQL语句的格式是已经规定好了的,需要查的数据也设置好了,缺的只是具体的那几个数据而已。所以用户能提供的只是数据,而且只能按需提供,无法更进一步做出影响数据库的其他举动来。

    完整项目案例
    点击这里 github

  • 相关阅读:
    Linux设备树(四 中断)
    Linux设备树(三 属性)
    Linux设备树(二 节点)
    责任链设计模式
    获取服务器内存和可用线程
    秒杀抢购思路解析
    Hystrix 用法及注解用法
    object is not an instance of declaring class
    sqlserver 截取字符串
    sqlserver 转化函数
  • 原文地址:https://www.cnblogs.com/kenx/p/13539197.html
Copyright © 2020-2023  润新知