JDBC介绍以及使用、PreparedStatement的原理、SQL注入

  • Post author:
  • Post category:其他




简介



JDBC概念:
  • JDBC就是使用java语言操作关系型数据库的一套API
  • 全称:(Java Database Connectivity)java数据库连接

    在这里插入图片描述


JDBC本质:
  • 官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口
  • 各个数据库厂商去实现这套接口,提供数据库驱动jar包
  • 我们可以使用这套接口(JABC)编程,真正执行代码的是驱动jar包中的实现类


JDBC好处:
  • 各数据库厂商使用相同的接口,Java代码不需要针对不同的数据库分别开发
  • 可以随时替换底层数据库,访问数据库的Java代码基本不变



JDBC快速入门

创建工程,导入驱动jar包

//1、注册驱动
       Class.forName("com.mysql.jdbc.Driver");

//2、获取连接
String url = "jdbc:mysql://localhost:3306/db_school";
String username = "root";
String password = "root";
Connection conn = DriverManager.getConnection(url,username,password);
//定义sql语句
String sql = "UPDATE account SET money = 2000 WHERE id = 1";

//获取sql对象
Statement stmt = conn.createStatement();

//执行sql 返回修改的行数
int count = stmt.executeUpdate(sql);
//处理返回的数据
System.out.println(count);

//释放资源
stmt.close();
conn.close();



JDBC的API



DriverManager(驱动管理类)


作用:
  • 注册驱动

  • Class.forName("com.mysql.jdbc.Driver");
    
  • 获取数据库连接

    • url:连接路径

      • 语法:jdbc:mysql://ip地址(域名):端口号/数据库名?参数值对1&参数值对2…
      • 细节:

        • 如果连接的是本地mysql服务器,并且mysql服务器默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称?参数值对
        • 配置useSSL=false参数,禁用安全连接方式,解决警告提示
    • user:用户名
    • password:密码

提示:

  • mysql 5之后的驱动包,可以省略注册驱动的步骤
  • 自动加载jar包中META-INF/services/java.sql.Driver中的驱动类


Connection


作用:
  • 获取执行sql的对象

    • 普通执行SQL对象

    • Statement createStatement()
      
    • 预编译SQL的执行SQL对象:防止sql注入

    • PreparedStatement preparedStatement(sql)
      
    • 执行存储过程的对象

    • CallableStatement prepareCall(sql)
      
  • 管理事务

    • mysql事务管理

      开启事务:BEGIN; /START TRANSACTION;
      提交事务:COMMIT;
      回滚事务:ROLLBACK;
      
      mysql默认自动提交事务
      
    • JDBC事务管理:Connection接口中定义了3个对应的方法

      开启事务:setAutoCommit(boolean autoCommit):true为自动提交事务;false为手动提交事务,即为开启事务
      提交事务:commit()
      回滚事务:rollback()
      


Statement
  • Startment的作用:

    • 执行SQL语句
  • 执行SQL语句

    int executeUpdate(sql):执行DML、DDL语句
    返回值:(1)DML语句影响的行数(2)DDL语句执行后,执行成功也可以返回0
    
    ResultSet executeQuery(sql):执行DQL语句
    返回值:ResultSet结果对象
    


ResultSet
  • ResultSet(结果集对象)作用

    • 封装了DQL查询语句的结果

      ResultSet stmt.executeQuery(sql):执行DQL语句,返回ResultSet对象
      
  • 获取查询结果

    boolean next():(1)将光标从当前位置向前移动一行(2)判断当前行是否为有效行
    返回值:
    	true:有效行,当前行有数据
    	false:无效行,当前行没数据
    
    xxx getXxx(参数):获取数据
    xxx:数据类型;如:int getInt(参数);String getString(参数)
    参数:
    	int:列的编号,从1开始
    	String:列的名称
    
  • 使用步骤

    • 游标向下移动一行,并判断该行是否有数据:next()

    • 获取数据:getXxx(参数)

      //循环判断游标是否是最后一行末尾
      while(rs.next()){
      	//获取数据
      	rs.getXxx(参数);
      }
      


PreparedStatement
  • PreparedStatement的作用:

    • 预编译SQL语句并执行:预防sql注入问题
  • SQL注入

    • SQL注入是通过操作输入来修改事先定义好的SQL语句,用以达到执行代码对服务器进行攻击的方式。
  • PreparedStatement

    • 获取PreparedStatement对象

      //sql语句中的参数值用?占位代替
      String = "select * from user where username = ? and password = ?";
      //通过Connection对象获取,并传入对应的SQL语句
      PreparedStatement pstmt = conn.prepareStatement(sql);
      
    • 设置参数

      PreparedSement对象:setXxx(参数1,参数2):给?赋值
      Xxx:数据类型
      参数:
      	参数1:?的位置编号,从1开始
      	参数2:?的值
      
    • 执行SQL

      executeUpdate();/executeQuery();
      //不再需要传递sql
      


PreparedStatement原理

PreparedStatement 好处:

  • 预编译SQL,性能更高
  • 防止SQL注入:

    将敏感字符进行转义

在这里插入图片描述

Java代码操作数据库流程如图所示:

  • 将sql语句发送到MySQL服务器端

  • MySQL服务端会对sql语句进行如下操作

    • 检查SQL语句

      检查SQL语句的语法是否正确。

    • 编译SQL语句。将SQL语句编译成可执行的函数。

      检查SQL和编译SQL花费的时间比执行SQL的时间还要长。如果我们只是重新设置参数,那么检查SQL语句和编译SQL语句将不需要重复执行。这样就提高了性能。

    • 执行SQL语句

接下来我们通过查询日志来看一下原理。

  • 开启预编译功能

    在代码中编写url时需要加上以下参数。而我们之前根本就没有开启预编译功能,只是解决了SQL注入漏洞。

    useServerPrepStmts=true
    
  • 配置MySQL执行日志(重启mysql服务后生效)

    在mysql配置文件(my.ini)中添加如下配置

    log-output=FILE
    general-log=1
    general_log_file="D:\mysql.log"
    slow-query-log=1
    slow_query_log_file="D:\mysql_slow.log"
    long_query_time=2
    
  • java测试代码如下:

     /**
       * PreparedStatement原理
       * @throws Exception
       */
    @Test
    public void testPreparedStatement2() throws  Exception {
    
        //2. 获取连接:如果连接的是本机mysql并且端口是默认的 3306 可以简化书写
        // useServerPrepStmts=true 参数开启预编译功能
        String url = "jdbc:mysql:///db1?useSSL=false&useServerPrepStmts=true";
        String username = "root";
        String password = "1234";
        Connection conn = DriverManager.getConnection(url, username, password);
    
        // 接收用户输入 用户名和密码
        String name = "zhangsan";
        String pwd = "' or '1' = '1";
    
        // 定义sql
        String sql = "select * from tb_user where username = ? and password = ?";
    
        // 获取pstmt对象
        PreparedStatement pstmt = conn.prepareStatement(sql);
    
        Thread.sleep(10000);
        // 设置?的值
        pstmt.setString(1,name);
        pstmt.setString(2,pwd);
        ResultSet rs = null;
        // 执行sql
        rs = pstmt.executeQuery();
    
        // 设置?的值
        pstmt.setString(1,"aaa");
        pstmt.setString(2,"bbb");
        // 执行sql
        rs = pstmt.executeQuery();
    
        // 判断登录是否成功
        if(rs.next()){
            System.out.println("登录成功~");
        }else{
            System.out.println("登录失败~");
        }
    
        //7. 释放资源
        rs.close();
        pstmt.close();
        conn.close();
    }
    
  • 执行SQL语句,查看

    D:\mysql.log

    日志如下:

    在这里插入图片描述

    上图中第三行中的

    Prepare

    是对SQL语句进行预编译。第四行和第五行是执行了两次SQL语句,而第二次执行前并没有对SQL进行预编译。



版权声明:本文为wocao_666原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。