up:: SQL注入攻击的解决策略

本篇博客还是沿用JDBC分析按部门查询员工功能这篇博客中的接口:

本篇博客篇幅很长,但核心内容就3点: (1)DbUtils; (2)【新增、更新、删除数据】的方法; (3)整体的包结构和类组织的套路;


一.封装DbUtils工具类:

【创建数据库连接】和【关闭数据库连接】的代码,需要经常用到。可以把这些需要重复使用的代码做成一个工具类,就是DbUtils工具类;

DbUtils类: 这个类没什么好说的,看下代码中的注释。

    package com.imooc.jdbc.common;
 
    import java.sql.*;
 
    public class DbUtils {
 
        /**
         * 创建数据库连接
         * @return
         * @throws SQLException
         * @throws ClassNotFoundException
         */
        public static Connection getConnection() throws SQLException, ClassNotFoundException {
            //加载并注册JDBC驱动,和,创建数据库连接会产生异常;
            // 这儿将异常抛出,而不是捕获;在实际调用这个getConnection()方法的时候,再对这个异常捕获
            Class.forName("com.mysql.cj.jdbc.Driver");
            Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/imooc?useSSL=false&useUincode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true", "root", "12345");
            return conn;
        }
 
        /**
         * 关闭数据库连接
         * 注:因为关闭方法中,在关闭前都做了判断是不是null,所以当实参是null的时候也是没问题的。即比如新增、删除、更新操作没有ResultSet,因为这儿ResultSet形参可以为null;所以,插叙,新增,删除,更新都可以调用这个方法;
         * @param rs:结果集对象;
         * @param stmt:执行SQL的Statement对象;这儿的形参是Statement类型,因为PrepareStatement是Statement的子接口,所以当实参是PrepareStatement类型的时候也没问题。
         * @param conn:连接对象;
         */
        public  static void closeConnection(ResultSet rs, Statement stmt,Connection conn){
            try {
                if (rs != null) {  // 如果rs != null,代表rs被实例化了,所以这儿需要关闭一下,进行释放;
                    rs.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                if (stmt != null) {  // 如果rs != null,代表stmt被实例化了,所以这儿需要关闭一下,进行释放;
                    stmt.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                if (conn != null && conn.isClosed() == false) {  // conn.isClosed()==false代表这个连接还没有关闭,还正在使用中;
                    conn.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
 
    }

二:新增、更新、删除实现

包结构:

1.新增(写操作):InsertCommand类

(1)新增操作需要使用PreparedStatement(因为 PreparedStatement可以进行参数化设置啊);

(2)新增使用PreparedStatement对象的executeUpdate()方法;

(3)executeUpdate()方法返回值:本次写操作所影响的数据库的行数;

(4)和查询不同,新增操作没有返回结果集;而是返回了一个(3)中的影响数据库的行数;

InsertCommand类:

    package com.imooc.jdbc.command;
 
    import com.imooc.jdbc.common.DbUtils;
 
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.SQLException;
    import java.util.Scanner;
 
    public class InsertCommand implements Command{
 
        @Override
        public void execute() {
            Scanner in = new Scanner(System.in);
            System.out.println("请输入员工编号:");
            int eno = in.nextInt();
            System.out.println("请输入员工姓名");
            String ename = in.next();
            System.out.println("请输入员工薪资:");
            Float salary = in.nextFloat();
            System.out.println("请输入隶属部门:");
            String dname = in.next();
            Connection conn = null;
            PreparedStatement pstmt = null;
            try {
                conn = DbUtils.getConnection();
                String sql = "insert into employee(eno,ename,salary,dname) values(?,?,?,?)";
                pstmt = conn.prepareStatement(sql);
                pstmt.setInt(1,eno);
                pstmt.setString(2,ename);
                pstmt.setFloat(3, salary);
                pstmt.setString(4, dname);
                int cnt = pstmt.executeUpdate();
                System.out.println(cnt);
                System.out.println(dname+"员工入职手续已办理。");
            } catch (SQLException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }finally {
                DbUtils.closeConnection(null, pstmt, conn);
            }
        }
    }
 

2.更新:UpdateCommand类

(1)更新也是使用PreparedStatement对象的executeUpdate()方法;

(2)executeUpdate()方法的返回值也是本次更新操作所影响的数据库的行数;

UpdateCommand类:

    package com.imooc.jdbc.command;
 
    import com.imooc.jdbc.common.DbUtils;
 
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.SQLException;
    import java.util.Scanner;
 
    public class UpdateCommand implements Command{
 
        @Override
        public void execute() {
            Scanner in = new Scanner(System.in);
            System.out.println("请输入员工编号:");
            int eno = in.nextInt();
            System.out.println("请输入员工薪资:");
            Float salary = in.nextFloat();
            Connection conn = null;
            PreparedStatement pstmt = null;
            Command command = null;
 
            try {
                conn = DbUtils.getConnection();
                String sql = "update employee set salary=? where eno=?";
                pstmt = conn.prepareStatement(sql);
                pstmt.setFloat(1,salary);
                pstmt.setInt(2,eno);
                int cnt = pstmt.executeUpdate();
                if (cnt == 1) {
                    System.out.println(eno + "员工薪资调整完毕。");
                } else {
                    System.out.println(eno + "员工不存在。");
                }
            } catch (SQLException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }finally {
                DbUtils.closeConnection(null,pstmt,conn);
            }
 
        }
    }
 

3.删除:DeleteCommand类

(1)删除也是使用PreparedStatement对象的executeUpdate()方法;

(2)executeUpdate()方法的返回值也是本次删除操作所影响的数据库的行数;可能返回1,也可能返回0(表示本次删除操作没有删除任何一条数据);

DeleteCommand类:

    package com.imooc.jdbc.command;
 
    import com.imooc.jdbc.common.DbUtils;
 
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.SQLException;
    import java.util.Scanner;
 
    public class DeleteCommand implements Command {
 
        /**
         * 删除员工
         */
        @Override
        public void execute() {
            Scanner in = new Scanner(System.in);
            System.out.println("请输入员工编号:");
            int eno = in.nextInt();
            Connection conn = null;
            PreparedStatement pstmt = null;
            Command command = null;
 
            try {
                conn = DbUtils.getConnection();
                String sql = "delete from employee where eno=?";
                pstmt = conn.prepareStatement(sql);
                pstmt.setInt(1,eno);
                int cnt = pstmt.executeUpdate();
                if (cnt == 1) {
                    System.out.println(eno + "员工离职成功。");
                } else {
                    System.out.println(eno + "员工不存在。");
                }
            } catch (SQLException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }finally {
                DbUtils.closeConnection(null,pstmt,conn);
            }
        }
    }
 

4.Command接口内容

    package com.imooc.jdbc.command;
 
    /**
     * 操作数据库的接口;
     */
    public interface Command {
        public void execute();
    }
 

5.入口类:HumanResourceApplication类

 
    package com.imooc.jdbc.hrapp;
 
    import com.imooc.jdbc.command.*;
 
    import java.util.Scanner;
 
    public class HumanResourceApplication {
        public static void main(String[] args) {
            System.out.println("1-查询部门员工");
            System.out.println("2-办理员工入职");
            System.out.println("3-调整薪资");
            System.out.println("4-员工离职");
            System.out.println("请选择功能:");
            Scanner in = new Scanner(System.in);
            Integer cmd = in.nextInt();
            Command command = null;// 将接口变量定义在外边,并初始化为null;在内部遇到不同操作的时候,再去实例化与之对应的类;
            switch (cmd){
                case 1: //查询部门员工
                    command = new PstmtQueryCommand();
                    command.execute();
                    break;
                case 2: //办理员工入职
                    command = new InsertCommand();
                    command.execute();
                    break;
                case 3: //调整薪资
                    command = new UpdateCommand();
                    command.execute();
                    break;
                case 4: //员工离职
                    command = new DeleteCommand();
                    command.execute();
                default:
            }
        }
    }