JDBC的PreparedStatement接口

声明此篇文章部分图片引用了百战尚学堂的

首先先给大家看一下JDBC的编写步骤,便于后边的讲解

什么是PreparedStatement对象:

继承自 Statement 接口,由 preparedStatement方法创建。PreparedStatement具有预编译SQL语句能力,所以PreparedStatement 对象比 Statement 对象的效率更高,由于实现了动态的参数绑定,所以可以防止 SQL 注入,所以我们一般都使用 PreparedStatement。

PreparedStatement执行sql语句的编写顺序:

1、获取Connection对象

2、编写sql语句

3、通过Connection对象的prepareStatement(sql)方法传递sql语句获取PreparedStatement对象

4、sql语句中的参数绑定,需要传递?的位置(?的位置从1开始)

5、PreparedStatement对象调用execute()方法执行sql语句

如何获取Connection对象:

可以通过该链接查看Connection对象的创建:

JDBC的Statement对象的使用https://blog.csdn.net/cccccccmmm/article/details/126415156

PreparedStatement对象的使用:

为了方便使用我们先写一个JdbcUtils工具类,通过该类可以实现数据库驱动的加载,Connection对象的创建,Connection对象、PreparedStatement对象、ResultSet对象的关闭

/**
 * Jdbc工具类
 */
public class JdbcUtils {
    private static String url;
    private static String name;
    private static String pwd;
    static{
        try(//通过字节输入流读取properties文件
            FileInputStream fis = new FileInputStream("src/jdbc.properties")){
            //实例化Properties对象
            Properties prop = new Properties();
            //解析读取到的properties文件
            prop.load(fis);
            //读取连接数据库的url
            url = prop.getProperty("url");
            //获取用户名
            name = prop.getProperty("username");
            //获取密码
            pwd = prop.getProperty("pwd");
            //获取数据库驱动全名
            String driver = prop.getProperty("driver");
            //通过反射机制加载数据库驱动
            Class.forName(driver);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    //获取数据库连接对象
    public static Connection getConnection(){
        Connection connection = null;
        try {
            connection = DriverManager.getConnection(url,name,pwd);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }

    //关闭连接对象
    public static void closeConnection(Connection connection){
        try {
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    //提交事务
    public static void commit(Connection connection){
        try {
            connection.commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    //事务回滚
    public static void rollback(Connection connection){
        try {
            connection.rollback();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    //关闭Statement对象
    public static void closeStatement(Statement statement){
        try {
            statement.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    //关闭ResultSet
    public static void closeResultSet(ResultSet resultSet){
        try {
            resultSet.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    //DML操作时关闭资源
    public static void closeResource(Statement statement,Connection connection){
        //先关闭Statement对象
        closeStatement(statement);
        //再关闭Connection对象
        closeConnection(connection);
    }

    //DQL操作时关闭资源
    public static void closeResource(ResultSet resultSet,Statement statement,Connection connection){
        //先关闭ResultSet
        closeResultSet(resultSet);
        //再关闭Statement
        closeStatement(statement);
        //最后关闭Connection
        closeConnection(connection);
    }
}

我们通过PreparedStatement对象实现一下添加数据到数据库表中的方法,那添加数据首先得有表把,所以我们这里创建一个表叫users,userid为主键列,之后的代码都是对这个users表进行操作

根据我上面给的编写顺序一步步写出来,这里主要注意的是sql语句中占位符由问号(?)占位,sql语句预编译过后,sql语句需要绑定参数,而绑定参数我们通过PreparedStatement对象的set参数类型(问号的位置(从1开始),参数)

例如:setString(1,username)

/**
     * 添加用户
     */
    public void insertUser(String username,int userage){
        Connection connection = null;
        PreparedStatement ps = null;
        try{
            //获取数据库连接对象
            connection = JdbcUtils.getConnection();
            //定义sql语句,?是PreparedStatement对象中绑定参数的占位符
            //?的位置是从1开始计数的
            String sql = "insert into users values(default,?,?)";
            //创建PreparedStatement
            ps = connection.prepareStatement(sql);
            //完成参数绑定
            ps.setString(1,username);
            ps.setInt(2,userage);
            //执行sql语句
            int ret = ps.executeUpdate();
            System.out.println(ret);
        }catch (Exception e){
            e.printStackTrace();
        }finally{
            JdbcUtils.closeResource(ps,connection);
        }
    }

此时我们再写一个测试类:

 运行过后就可以到数据库中查看表中是否有添加数据了

什么是SQL注入?

可以用它来从数据库获取敏感信息、利用数据库的特性执行添加用户、导出文件等一系列恶意操作,甚至有可能获取数据库乃至系统用户最高权限。

造成SQL注入的原因

程序没有有效过滤用户的输入,使攻击者成功的向服务器提交恶意的 SQL 脚本,程序在接收后错误的将攻击者的输入作为 SQL 语句的一部分执行,导致原始的查询逻辑被改变,执行了攻击者精心构造的恶意 SQL 语句。

为什么PreparedStatement对象可以防止SQL注入?

为什么说PreparedStatement对象可以避免SQL注入呢?这就是因为PreparedStatement有sql语句预编译的操作,我们刚刚上面对PreparedStatement对象进行了使用,我们会发现sql语句和需要传递的参数是分离的,这就不会导致改变我们查询代码的逻辑

我们使用Statement对象给大家看看SQL注入是怎么样的: 

现在我们表中有这么些数据

我们对users表进行简单的查询操作,条件是username为我们给定的参数并且userage为我们给定的参数

我们给定的参数如果这么写,那么就改变了我们查询语句的逻辑,我们想要的是查询对应的用户信息和年龄,我们这么写直接就可以查询到所有的用户信息

 我们把这个sql语句打印出来看看:

此时我们会发现我们查询了条件为username = 'oldlu' 或者 1=1的用户信息,1=1永远是正确的所以这就改变了我们的查询逻辑,直接查出了所有用户的信息,后面的--代表注释

因为Statement对象是通过字符串拼接的方式执行sql语句得,所以如果有人别有用心的话,就可以通过sql注入的方式改变我们的查询逻辑,查询出所有人的信息

但是PreparedStatement对象不会有这个问题,为什么呢?刚刚说过了,因为该对象将sql语句和传递的参数分离了,实现动态绑定参数的方式

我们再实现一个无法sql注入的方法,该方法就是使用PreparedStatement对象实现的

此时该方法执行的顺序是:

先传递sql语句:"select * from users where username = ? and userage = ?"

然后绑定参数,问号会自动识别参数类型,如果为字符串类型会自动加上单引号

此时我们就会发现我们不管怎样写都不会再有sql注入的问题,mysql会将他视为就是一个参数,不会再改变查询的逻辑

public void noSqlInject(String username,int userage){
        Connection connection = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try{
            //获取连接器对象
            connection = JdbcUtils.getConnection();
            //编写sql语句
            String sql = "select * from users where username = ? and userage = ?";
            //获取PreparedStatement对象,并且预编译sql语句
            ps = connection.prepareStatement(sql);
            //动态绑定sql语句
            ps.setString(1,username);
            ps.setInt(2,userage);
            //执行sql语句
            rs = ps.executeQuery();
            while(rs.next()){
                int userid = rs.getInt("userid");
                String name = rs.getString("username");
                int age = rs.getInt("userage");
                System.out.println(userid  +" "+ username +" "+ userage);
            }
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            JdbcUtils.closeResource(rs,ps,connection);
        }
    }

编译代码之后没有查询结果

 记住PreparedStatement对象执行sql语句的编写顺序:

1、获取Connection对象

2、编写sql语句

3、通过Connection对象的prepareStatement(sql)方法传递sql语句获取PreparedStatement对象

4、sql语句中的参数绑定,需要传递?的位置(?的位置从1开始)

5、PreparedStatement对象调用execute()方法执行sql语句

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值