JDBC Statement与PreparedStatement

2018-03-11  本文已影响0人  pianpianboy

JDBC预编译sql语句处理

时间:20180311


使用Statement与PreparedStatement执行DDL与DML的区别?

/**
 * jdbc通用方法
 * @author mengjie
 *
 */
public class JdbcUtil {
    //url
    private static String url = "jdbc:mysql://localhost:3306/day16";
    //user 
    private static String user  = "root";
    //password
    private static String password = "root";
    
    /**
     *z只注册一次驱动,静态代码块 
     */
    static {
        //注册驱动程序
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    
    /**
     * 获取连接的方法
     * @throws SQLException 
     */
    public static Connection getConnection() {
        try {
            Connection conn = DriverManager.getConnection(url, user, password);
            return conn;
        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * 释放资源的方法
     */
    public static void close(Statement stmt, Connection conn) {
        if(stmt != null) {
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        }
        
        if(conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        }
    }
    
    /**
     * 释放资源的方法
     */
    public static void close(ResultSet rs, Statement stmt, Connection conn) {
        if(rs!=null) {
            try {
                rs.close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
        if(stmt != null) {
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        }
        
        if(conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        }
    }
}
/**
 * 使用PreparedStatement执行CRUD操作
 * 执行预编译的sql语句
 * @author mengjie
 *
 */
public class Demo1 {
    public static void main(String[] args) {
        //insert();
        //update();
        query();
    }

    private static void query() {
        Connection conn = null;
        PreparedStatement stmt = null;
        ResultSet res = null;
        try{
            //1)建立连接
            conn = JdbcUtil.getConnection();
            //2) 创建sql
            String sql = "select * from student where id = ?";
            //3) 创建PreparedStatement,预编译sql语句
            stmt = conn.prepareStatement(sql);
            //4) 给参数赋值
            /**
             * 注意参数的位置,一定要对应字段的位置
             */
            stmt.setInt(1, 5);
            //5) 执行sql
            res = stmt.executeQuery();
            while(res.next()) {
                int id = res.getInt("id");
                String name = res.getString("name");
                int age = res.getInt("age");
                System.out.println(id + "\t" +name+"\t"+age);
            }
        }catch(Exception e) {
            e.printStackTrace();
        }finally {
            JdbcUtil.close(stmt, conn);
        }
    }

    private static void update() {
        /**
         * 修改
         */
        Connection conn = null;
        PreparedStatement stmt = null;
        try{
            //1)建立连接
            conn = JdbcUtil.getConnection();
            //2) 创建sql
            String sql = "update student set name = ?, age = ? where id = ?";
            //3) 创建PreparedStatement,预编译sql语句
            stmt = conn.prepareStatement(sql);
            //4) 给参数赋值
            /**
             * 注意参数的位置,一定要对应字段的位置
             */
            stmt.setInt(3, 4);
            stmt.setString(1, "wanzhe");
            stmt.setInt(2, 100);
            //5) 执行sql
            int n = stmt.executeUpdate();
            System.out.println("影响了"+ n + "行");
        }catch(Exception e) {
            e.printStackTrace();
        }finally {
            JdbcUtil.close(stmt, conn);
        }
    }

    private static void insert() {
        /**
         * 插入
         */
        Connection conn = null;
        PreparedStatement stmt = null;
        try {
            //1)获取连接
            conn = JdbcUtil.getConnection();
            //2)准备sql//?号是参数的占位符,一个问好代表是一个参数。
            String sql = "insert into student(id,name,age) values(?,?,?)";//
            //3)创建PreparedStatement,预编译sql语句
            stmt = conn.prepareStatement(sql);//预编译sql语句(sql语法和权限的检查),
            //4)给参数赋值
            /**
             * 参数一: 参数的位置。从1开始
             * 参数二:参数值
             */
            stmt.setInt(1, 5);
            stmt.setString(2, "里斯");
            stmt.setInt(3, 40);
            //5)发送参数到数据库,执行sql,
            //executeUpdate方法中并没有参数,是将参数和sql组装起来,并执行
            int n = stmt.executeUpdate();
            System.out.println("影响了"+ n + "行");
        }catch(Exception e){
            e.printStackTrace( );
        }finally {
            //关闭资源,而且不用更改close方法(此时传递的stmt是preparedStatement对象),不用重载
            //实现参数为preparedStatement对象的close方法
            //原因是statement是preparedstatement类的父类
            JdbcUtil.close(stmt, conn);
        }
    }
}

为什么要使用PreparedStatement?

1.PreparedStatement接口继承了Statement,PreparedStatement实例包含已编译的SQL语句,所以其执行速度要快于Statement对象。
2.作为Statement的子类,PreparedStatment继承了Statement的所有功能。三种方法execute,executeQuery和executeUpdate已被更改以使之不再需要参数。
3.在JDBC应用中,在任何时候都不要是使用Statement,原因如下:
  1)、代码的可读性和可维护性。Statement需要不断地拼接,而PreparedStatement不会。
  2)、PreparedStatement尽最大可能提高性能。DB有缓存机制,相同的预编译语句再次被调用不会再次需要编译。
  3)、最重要的一点是极大的提高了安全性。Statement容易被SQL注入,而PreparedStatement传入的内容和参数不会和sql语句发生任何匹配关系。

关于statement和PreparedStatement的安全性做具体分析如下:

例如用户登陆的案例
在数据库中操作:

--创建用户表
CREATE TABLE user_list(
    id INT PRIMARY KEY AUTO_INCREMENT,
    NAME VARCHAR(20),
    PASSWORD VARCHAR(20)
)
--插入数据
INSERT INTO user_list(NAME,PASSWORD) VALUES('eric','123456');
INSERT INTO user_list(NAME,PASSWORD) VALUES('jacky','654321');
SELECT * FROM user_list;

如何判断登陆是否成功呢?根据下列sql:

--登陆成功
SELECT * FROM user_list WHERE NAME='eric' AND PASSWORD='123456'
能成功登陆的情况

但是在数据库sql中存在如下情况:

SELECT * FROM user_list WHERE 1=1; --1=1; 恒成立
sql恒成立的情况

登陆失败的情况

SELECT * FROM user_list WHERE NAME='rose' AND PASSWORD='123456'

可将上述sql更改为恒成立情况,注意-- 前后的空格

SELECT * FROM user_list WHERE NAME ='rose' OR 1=1 -- ' AND PASSWORD='123456';
sql注入的情况

以上都是在客户端中执行的sql语句进行的测试对比
分割线


以下都是在代码中执行的sql语句进行的测试对比

分别在Statement和PreparedStatement中执行带有sql注入的sql,验证Statement和PreparedStatement的安全性。

public class Demo2 {
    private static Connection conn = null;
    private static Statement stmt = null;
    private static ResultSet res = null;
    public static void main(String[] args) throws Exception {
                String username = "rose";
                String password = "123456";
        conn = JdbcUtil.getConnection();
        stmt = conn.createStatement();
        String sql = "SELECT * FROM user_list WHERE NAME =' "+username+" ' AND PASSWORD=' "+password+" '";
        res = stmt.executeQuery(sql);
        if(res.next()) {
            System.out.println("登陆成功");
        }else {
            System.out.println("登陆失败");
        }
    }
}

结果:
登陆失败

public class Demo2 {
    private static Connection conn = null;
    private static Statement stmt = null;
    private static ResultSet res = null;
    public static void main(String[] args) throws Exception {
                String username = "rose' OR 1=1 -- ";
                String password = "123456";
        conn = JdbcUtil.getConnection();
        stmt = conn.createStatement();
        String sql = "SELECT * FROM user_list WHERE NAME =' "+username+" ' AND PASSWORD=' "+password+" '";
        res = stmt.executeQuery(sql);
        if(res.next()) {
            System.out.println("登陆成功");
        }else {
            System.out.println("登陆失败");
        }
    }
}

结果:
登陆成功

public class Demo2 {
    private static Connection conn = null;
    private static PreparedStatement stmt = null;
    private static ResultSet res = null;
    public static void main(String[] args){
        conn = JdbcUtil.getConnection();
        String sql = "SELECT * FROM user_list WHERE NAME = ? AND PASSWORD= ?";
        try {
            stmt = conn.prepareStatement(sql);
            stmt.setString(1, "rose' OR 1=1 -- ");
            stmt.setString(2, "123456");
            res = stmt.executeQuery();
            if(res.next()) {
                System.out.println("登陆成功");
            }else {
                System.out.println("登陆失败");
            }
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }finally {
            JdbcUtil.close(res,stmt,conn);
        }
    }
}

结果:
登陆失败

解析PreparedStatement是如何防止SQL注入的?

Statement的情况

说明PreparedStatement传入的内容不会和sql语句发生任何关系,这也是statement中的sql(静态sql)能被注入sql的原因。

上一篇下一篇

猜你喜欢

热点阅读