JDBC(一)

目录

一.JDBC概述

1.1 JDBC概念

1.2 JDBC优点

二.JDBC快速入门

2.1 编写Java代码步骤

三.JDBC API详解

3.1 DriverManeger

3.1.1 作用

3.2 Connection

3.2.1 作用

3.2.2 获取执行SQL的对象

3.2.3 事务管理

3.3 Statement

3.3.1 作用

3.3.2 案例

3.4 ResultSet

3.4.1 概述

3.4.2 案例

3.5 PreparedStatement

3.5.1 SQL注入

3.5.2 模拟SQL注入问题

3.5.3  PreparedStatement概述

3.5.4 使用PreparedStatement解决sql注入

3.5.5  PreparedStatement原理


一.JDBC概述

在开发中我们使用的是Java语言,那么势必要通过Java语言操作数据库中的数据。这就是接下来要学习的JDBC。

1.1 JDBC概念

JDBC 就是使用Java操作关系型数据库的一套API 。
全称:( Java DataBase Connectivity ) Java 数据库连接
为了做到同一套Java代码操作不同的关系型数据库,sun公司就指定了一套标准接口 :JDBC,JDBC中定义了所有操作关系型数据库的规则。众所周知接口是无法直接使用的,我们需要使用接口的实现类,而这套实现类称之为驱动,就由各自的数据库厂商给出。

1.2 JDBC优点

各数据库厂商使用相同的接口,Java代码不需要针对不同数据库 分别开发 。
可随时替换底层数据库,访问数据库的Java代码基本不变 。
以后编写操作数据库的代码只需要面向JDBC(接口),操作哪儿个 关系型数据库就需要导入该数据库的驱动包,如需要操作MySQL数 据库,就需要再项目中导入MySQL数据库的驱动包。如下图就是 MySQL驱动包。
                                            

二.JDBC快速入门

先来看看通过Java操作数据库的流程
第一步:编写Java代码 
第二步:Java代码将SQL发送到MySQL服务端 
第三步:MySQL服务端接收到SQL语句并执行该SQL语句 
第四步:将SQL语句执行的结果返回给Java代码

2.1 编写Java代码步骤

第一步:
在工程模块中导入驱动jar包
第二步:
注册驱动  Class.forName("com.mysql.jdbc.Driver");
第三步:
获取连接  Connection conn = DriverManager.getConnection(url, username, password);
第四步:
定义SQL语句  String sql = “update…” ;
第五步:
获取执行SQL对象  Statement stmt = conn.createStatement();
第六步:
执行SQL  int result = stmt.executeUpdate(sql);
第七步:
处理返回结果
第八步:
释放资源  stml.close();
                 con.close();

三.JDBC API详解

3.1 DriverManeger

3.1.1 作用

1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
2.获取数据库连接对象
String userName = "root";
String password = "root";
String url = "jdbc:mysql://127.0.0.1:3306/db1?useSSL=false";
Connection con = DriverManager.getConnection(url,userName,password);
参数说明:
userName :用户名 
password :密码
url : 连接路径 
语法:jdbc: mysql://ip地址(域名):端口号/数据库名称?参数键值对1&参数键值对2… 。
示例:jdbc: mysql://127.0.0.1:3306/db1 。
3.注意
①如果连接的是本机mysql服务器,并且mysql服务默 认端口是3306,则url可以简写为:jdbc:mysql:///数 据库名称?参数键值对 。
②在URL后面配置 useSSL=false 参数,禁用安全连接方式,解决警告提示 。

3.2 Connection

3.2.1 作用

1.获取执行SQL的对象
2.管理事务

3.2.2 获取执行SQL的对象

1.普通执行SQL对象 入门案例中就是通过该方法获取的执行对象。 
2.预编译SQL的执行SQL对象:防止SQL注入 通过这种方式获取的 PreparedStatement SQL语句执行对象是 我们一会重点要进行讲解的,它可以防止SQL注入。 
3.执行存储过程的对象 通过这种方式获取的 CallableStatement 执行对象是用来执行 存储过程的,而存储过程在MySQL中不常用,所以这个我们将 不进行讲解。

3.2.3 事务管理

1.先回顾一下MySQL事务管理的操作: 
开启事务 : BEGIN或者 START TRANSACTION; 
提交事务 : COMMIT; 
回滚事务 : ROLLBACK; MySQL默认是自动提交事务
2.接下来学习JDBC事务管理的方法。 Connection接口中定义了3个对应的方法: 
开启事务 :
con.setAutoCommit(false);
参与autoCommit 表示是否自动提交事务,true表示自动提交事 务,false表示手动提交事务。而开启事务需要将该参数设为为 false。
提交事务 
con.commit();
回滚事务
con.rollback();
3.案例
​
/**
* JDBC API 详解:Connection
*/
public class JDBCDemo3_Connection {
    public static void main(String[] args) throws Exception {
        //1.注册驱动,mysql5之后的驱动包,可以省略注册驱动的步骤
        Class.forName("com.mysql.jdbc.Driver");
        //2.获取连接对象
        String userName = "root";
        String password = "root";
        //useSSL=false代表禁用安全提示
        String url = "jdbc:mysql:///db1?useSSL=false";//连接的是本机的数据库的话,可以不加IP地址或端口号。                localhost是本机的IP地址。
        Connection con = DriverManager.getConnection(url,userName,password);
        //3.定义sql
        String sql1 = "update account set money=2000 where id=1";
        String sql2 = "update account set money=2000 where id=2";
        //4.获取执行sql的对象Statement
        Statement statement = con.createStatement();
        try {
        //开启事务
        con.setAutoCommit(false);
        //5.执行sql
        int count1 = statement.executeUpdate(sql1);//该方法的返回结果是account表中受影响的行数
        //6.处理结果
        System.out.println(count1);
        //制造个错误
        int a = 8/0;
        //5.执行sql
        int count2 = statement.executeUpdate(sql2);
        //6.处理结果
        System.out.println(count2);
        //提交事务
       con.commit();
        } catch (Exception e) {
        con.rollback();//如果代码出现了异常,就回滚到事务开启前的状态
        e.printStackTrace();
        }
        //7.释放资源
        statement.close();
        con.close();
    }
}

3.3 Statement

3.3.1 作用

Statement对象的作用就是用来执行SQL语句。而针对不同类型的 SQL语句使用的方法也不一样。
1.执行DDL、DML语句:
int executeUpdate(String sql);
2.执行DQL语句
ResultSet executeUpdate(String sql);
注意:该方法涉及到了 ResultSet 对象,而这个对象我们还没有学习,一会在3.4节重点讲解。

3.3.2 案例

1.DDL:
2.DML;

3.4 ResultSet

3.4.1 概述

ResultSet(结果集对象)作用: 封装了SQL查询语句的结果。 
而执行了DQL语句后就会返回该对象,对应执行DQL语句的方法如 下:
那么我们就需要从 ResultSet 对象中获取我们想要的数据。 ResultSet 对象提供了操作查询结果数据的方法,如下:
boolean next() 
        将光标从当前位置向前移动一行 
        判断当前行是否为有效行 
方法返回值说明: 
        true : 有效航,当前行有数据 
        false : 无效行,当前行没有数据 
xxx getXxx(参数):获取数据 
        xxx : 数据类型;如: int getInt(参数) ;String getString(参 数) 
参数 
        int类型的参数:列的编号,从1开始 
        String类型的参数: 列的名称
如下图为执行SQL语句后的结果:
一开始光标指定于第一行前,如图所示红色箭头指向于表头行。当 我们调用了 next() 方法后,光标就下移到第一行数据,并且方法 返回true,此时就可以通过 getInt("id") 获取当前行id字段的 值,也可以通过 getString("name") 获取当前行name字段的 值。如果想获取下一行的数据,继续调用 next() 方法,以此类 推。

3.4.2 案例

3.5 PreparedStatement

3.5.1 SQL注入

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

3.5.2 模拟SQL注入问题

1.tb_user表:

2.Java代码:

//用户登录

public class JDBCDemo6_UserLogin {

    @Test

    public void testUserLogin() throws Exception {

        //1.注册驱动

        Class.forName("com.mysql.jdbc.Driver");



        //2.获取连接对象

        String userName = "root";

        String password = "root";

        String url = "jdbc:mysql://127.0.0.1:3306/db1?useSSL=false";

        Connection con = DriverManager.getConnection(url,userName,password);



        //接收用户输入的用户名和密码

        String name = "hcfshjbcjashn";

        String psw = "' or '2' = '2";



        //3.定义sql,字符串拼接,分成三部分看

        String sql = "select * from tb_user where username='"+name+"' and password='"+psw+"'";



        //4.获取Statement对象

        Statement statement = con.createStatement();



        //5.执行sql

        ResultSet resultSet = statement.executeQuery(sql);



        //6.处理结果

        if(resultSet.next()){

        System.out.println("登录成功");

        }else{

        System.out.println("登陆失败");

        }



        //7.关闭资源

        resultSet.close();

        statement.close();

        con.close();

    }

}

上面代码是将用户名和密码拼接到sql语句中,拼接后的sql语句如 下:

从上面语句可以看出条件 username = 'sjdljfld' and password = '' 不管是否满足,而 or 后面的 '1' = '1' 是始终 满足的,最终条件是成立的,就可以正常的进行登陆了。

3.5.3  PreparedStatement概述

1.作用

①预编译SQL语句并执行

②预防SQL注入问题

2.步骤

// SQL语句中的参数值,使用?占位符替代 

String sql = "select * from user where username = ? and password = ?"; 

// 通过Connection对象获取,并传入对应的sql语句 

PreparedStatement pstmt = conn.prepareStatement(sql);

设置参数值 上面的sql语句中参数使用 ? 进行占位,在之前之前肯定要设置 这些 ? 的值。

pstmt.setXXX(1,name);    

pstmt.setXXX(2,pwd);

3.5.4 使用PreparedStatement解决sql注入

/**

* JDBC API:PreparedStatement

*/

public class JDBCDemo7_PreparedStatemrnt {

    @Test

    public void testUserLogin() throws Exception {

        //1.注册驱动

        Class.forName("com.mysql.jdbc.Driver");



        //2.获取连接对象

        String userName = "root";

        String password = "root";

        //useServerPrepStmts=true代表开启预编译功能

        String url = "jdbc:mysql://127.0.0.1:3306/db1?useSSL=false";

        Connection con = DriverManager.getConnection(url,userName,password);



        //接收用户输入的用户名和密码

        String name = "zhangsan";

        String psw = "123";



        //3.定义sql,字符串拼接,分成三部分看

        String sql = "select * from tb_user where username=? and password=?";



        //4.获取PreparedStatement对象并添加sql的参数

        PreparedStatement ps = con.prepareStatement(sql);

        ps.setString(1,name);

        ps.setString(2,psw);



        //5.执行sql

        ResultSet resultSet = ps.executeQuery();

        

        //6.处理结果

        if(resultSet.next()){

        System.out.println("登录成功");

        }else{

        System.out.println("登陆失败");

        }



        //7.关闭资源

        resultSet.close();

        ps.close();

        con.close();

    }

}

执行上面语句就可以发现不会出现SQL注入漏洞问题了。那么 PreparedStatement又是如何解决的呢?它是将特殊字符进行了转 义,转义的SQL如下:

3.5.5  PreparedStatement原理

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

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

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

                检查SQL语句

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

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

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

        5.执行SQL语句

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

        1.开启预编译功能 

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

                

        2.配置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测试代码如下:

/**

* JDBC API:PreparedStatement

*/

public class JDBCDemo7_PreparedStatemrnt {

    @Test

    public void testUserLogin() throws Exception {

        //1.注册驱动

        Class.forName("com.mysql.jdbc.Driver");


        //2.获取连接对象

        String userName = "root";

        String password = "root";

        //useServerPrepStmts=true代表开启预编译功能

        String url = "jdbc:mysql://127.0.0.1:3306/db1?useSSL=false";

        Connection con = DriverManager.getConnection(url,userName,password);


        //接收用户输入的用户名和密码

        String name = "zhangsan";

        String psw = "123";


        //3.定义sql,字符串拼接,分成三部分看

        String sql = "select * from tb_user where username=? and password=?";

        //4.获取PreparedStatement对象并添加sql的参数

        PreparedStatement ps = con.prepareStatement(sql);

        ps.setString(1,name);

        ps.setString(2,psw);


        //5.执行sql

        ResultSet resultSet = ps.executeQuery();


        //6.处理结果

        if(resultSet.next()){

        System.out.println("登录成功");

        }else{

        System.out.println("登陆失败");

        }


        //7.关闭资源

        resultSet.close();

        ps.close();

        con.close();

    }

}

查看 D:\mysql.log 日志如下:

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

总结:

①在获取PreparedStatement对象时,将sql语句发送给mysql 服务器进行检查,编译(这些步骤很耗时) 

②执行时就不用再进行这些步骤了,速度更快 

③如果sql模板一样,则只需要进行一次检查、编译

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

真滴book理喻

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值