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(); } } } } }