Statement和preparedStatement之间的区别?

根据JDBC模拟用户功能的实现:
loginUser.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/mydatabase
user=root
password=333

 

package com.java.JDBC;


import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Scanner;


/*
实现功能:
    1 需求:模拟用户登录功能的实现。
    2 业务描述:
        程序运行的时候,提供一个输入的入口,可以让用户输入用户名和密码。
        用户输入用户名和密码之后,提交信息,java程序收集用户信息
        Java程序连接数据库验证用户名和密码是否合法。
        合法:显示登陆成功。
        不合法:显示登陆失败。
    3 数据的准备
        在实际开发中,表的设计会使用专业的建模工具,我们这里安装一个建模工具:PowerDesigner
        使用PD工具来进行数据库表的设计。(参见user-login.sql脚本)
        
    4 当前程序存在的问题:
        用户名:fdsa
        密码:fdsa' or '1'='1
        登录成功
        这种现象被称为SQL注入(安全隐患)。(黑客经常使用)
        
    5 导致SQL注入的根本原因是什么?
        select * from t_user where loginName = 'fdsa' and loginpwd = 'fdsa' or '1'='1';
        用户输入的信息中含有sql语句的关键字,并且这些关键字参与sql语句的编译过程,导致sql语句的原意被扭曲,从而达到sql注入。
    
    
*/
public class JDBCTest06 {
    public static void main(String[] args) {
        // 初始化一个界面
        Map<String,String> userLoginInfo = initUI();
        // 验证用户名和密码
        boolean loginSuccess = login(userLoginInfo);
        System.out.println(loginSuccess == true ? "登陆成功" : "登陆失败");
    }
    /**
     * 用户登录
     * @param userLoginInfo 用户登录信息
     * @return true表示成功  false表示失败
     */
    private static boolean login(Map<String, String> userLoginInfo) {
        // JDBC代码
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        boolean result = false;
        ResourceBundle rb = ResourceBundle.getBundle("loginUser");
        
        String driver = rb.getString("driver");
        String url = rb.getString("url");
        String user = rb.getString("user");
        String password = rb.getString("password");
    
        String loginName = userLoginInfo.get("loginName");
        String loginpwd = userLoginInfo.get("password");
        
        try {
            // 1 注册驱动
            // Class.forName("com.mysql.jdbc.Driver");
            Class.forName(driver);
            // 2 获取连接
            // conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase","root","333");
            conn = DriverManager.getConnection(url,user,password);
            // 3 获取数据库操作对象
            stmt = conn.createStatement();
            // 4 执行sql语句
            String sql = "select * from t_user where loginName = '" + loginName + "' and loginpwd = '" + loginpwd +"'";
            // 以上正好完成了sql语句的拼接,以下代码的含义是,发送sql语句给DBMS进行sql编译。
            // 正好将用户提供的“非法信息”编译进去。导致了原sql语句的含义被扭曲了。
            System.out.println(sql);
            rs = stmt.executeQuery(sql);
            // 5 处理返回结果集
            if (rs.next()){
                result = true;
            }
            
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 6 释放资源
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
    
            if (stmt != null) {
                try {
                    stmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
    
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }
    
    /**
     * 初始化用户界面
     * @return 用户输入的用户名和密码等登陆信息
     */
    private static Map<String, String> initUI() {
        Scanner scanner = new Scanner(System.in);
        System.out.print("用户名: ");
        String username = scanner.nextLine();
        
        System.out.print("密码: ");
        String password = scanner.nextLine();
        
        Map<String,String> userLoginInfo = new HashMap<>();
        userLoginInfo.put("loginName",username);
        userLoginInfo.put("password",password);
        return userLoginInfo;
    }
}

 

 

案例7:如何解决JDBC注入问题及Statement和PreparedStatement之间的区别?

package com.java.JDBC;


import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Scanner;
/*
1 解决sql注入问题
    只要用户提供的信息不参与sql语句的编译过程,问题就解决了。
    即使用户提供的信息中心含有sql语句的关键字,但是没有参与编译,不起作用。
    要想用户信息不参与sql语句的编译,必须使用java.sql.PreparedStatement
    PreparedStatement接口继承了java.sql.Statement
    PreparedStatement是属于预编译的数据库操作系统
    PreparedStatement原理是:预先对sql语句的框架进行编译,然后再给sql语句传“值”
    
2 解决sql注入的关键是什么?
    用户提供的信息中即使含有sql语句的关键字,但是这些关键字并没有参与编译,不起作用。
    
3 对比一下Statement和PreparedStatement?
    - Statement存在sql注入问题,PreparedStatement解决了sql注入问题。
    - Statement是编译一次执行一次,PreparedStatement是编译一次,可执行n次。PreparedStatement效率较高一些。
    - PreparedStatement会在编译阶段做类型的安全检查。
    
    综上所述:PreparedStatement使用较多,只有极少数的情况下需要使用Statement


4 什么情况下必须使用Statement呢?
    业务方面要求必须支持SQL注入的时候。
    Statement支持sql注入,凡是业务方面要求是需要进行sql语句拼接的,必须使用Statement。
    
*/
public class JDBCTest07 {
    public static void main(String[] args) {
        // 初始化一个界面
        Map<String,String> userLoginInfo = initUI();
        // 验证用户名和密码
        boolean loginSuccess = login(userLoginInfo);
        System.out.println(loginSuccess == true ? "登陆成功" : "登陆失败");
    }
    /**
     * 用户登录
     * @param userLoginInfo 用户登录信息
     * @return true表示成功  false表示失败
     */
    private static boolean login(Map<String, String> userLoginInfo) {
        // JDBC代码
        Connection conn = null;
        PreparedStatement ps = null; // 这里使用PreparedStatement
        ResultSet rs = null;
        boolean result = false;
        ResourceBundle rb = ResourceBundle.getBundle("loginUser");
        
        String driver = rb.getString("driver");
        String url = rb.getString("url");
        String user = rb.getString("user");
        String password = rb.getString("password");
        
        String loginName = userLoginInfo.get("loginName");
        String loginpwd = userLoginInfo.get("password");
        
        try {
            // 1 注册驱动
            // Class.forName("com.mysql.jdbc.Driver");
            Class.forName(driver);
            
            // 2 获取连接
            // conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase","root","333");
            conn = DriverManager.getConnection(url,user,password);
            
            // 3 获取预编译的数据库操作对象
            // sql语句的框子,其中一个?,表示一个占位符,一个?将来接收一个“值”,注意:占位符不能使用单引号括起来
            String sql = "select * from t_user where loginName = ? and loginpwd = ?";
            
            // 程序执行到此处,会发送slq语句框子给DBMS,然后DBMS进行sql语句的预先编译。
            ps = conn.prepareStatement(sql);
            
            // 给占位符?传值(第1个问号下标是1,第2个问号下标是2,JDBC中所有下标从1开始)
            ps.setString(1,loginName);
            ps.setString(2,loginpwd);
            
            // 4 执行sql语句
            rs = ps.executeQuery();
            
            // 5 处理返回结果集
            if (rs.next()){
                result = true;
            }
            
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 6 释放资源
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            
            if (ps != null) {
                try {
                    ps.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }
    
    /**
     * 初始化用户界面
     * @return 用户输入的用户名和密码等登陆信息
     */
    private static Map<String, String> initUI() {
        Scanner scanner = new Scanner(System.in);
        System.out.print("用户名: ");
        String username = scanner.nextLine();
        
        System.out.print("密码: ");
        String password = scanner.nextLine();
        
        Map<String,String> userLoginInfo = new HashMap<>();
        userLoginInfo.put("loginName",username);
        userLoginInfo.put("password",password);
        return userLoginInfo;
    }
}

 

 

 
案例8:使用Statement可以对查询结果集进行升序和降序,但是用PreparedStatement不行,因为他会预编译。
package com.java.JDBC;


import java.sql.*;
import java.util.Scanner;


public class JDBCTest08 {
    public static void main(String[] args) {
        /*// 用户在控制台输入desc就是降序,输入asc就是升序
        Scanner s = new Scanner(System.in);
        System.out.println("请输入desc或asc,desc表示降序,asc表示升序");
        System.out.print("请输入: ");
        String keyWords = s.nextLine();
        
        // 执行sql
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            // 注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            // 获取数据库连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase","root","333");
            // 获取数据库对象
            String sql = "select ename from emp order by ename ?";
            ps = conn.prepareStatement(sql);
            // 执行sql语句
            ps.setString(1,keyWords);
            // 处理查询结果集
            rs = ps.executeQuery();
            // 遍历结果集
            while (rs.next()){
                String ename = rs.getString("ename");
                System.out.println(ename);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 释放资源
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (ps != null) {
                try {
                    ps.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }*/
        
        
        // 用户在控制台输入desc就是降序,输入asc就是升序
        Scanner s = new Scanner(System.in);
        System.out.println("请输入desc或asc,desc表示降序,asc表示升序");
        System.out.print("请输入: ");
        String keyWords = s.nextLine();
    
        // 执行sql
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try {
            // 注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            // 获取数据库连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase","root","333");
            // 获取数据库对象
            stmt = conn.createStatement();
            // 执行sql语句
            String sql = "select ename from emp order by ename " + keyWords;
            // 处理查询结果集
            rs = stmt.executeQuery(sql);
            // 遍历结果集
            while (rs.next()){
                String ename = rs.getString("ename");
                System.out.println(ename);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 释放资源
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (stmt != null) {
                try {
                    stmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

 

posted @ 2020-09-11 20:36  xlwu丶lz  阅读(215)  评论(0编辑  收藏  举报