【MySQL】JDBC 编程详解

JDBC 编程详解

  • 一. 概念
  • 二. JDBC 工作原理
  • 三. JDBC 使用
    • 1. 创建项目
    • 2. 引入依赖
    • 3. 编写代码
      • (1). 创建数据源
      • (2). 建立数据库连接
      • (3). 创建 SQL
      • (4). 执行 SQL
      • (5). 遍历结果集
      • (6). 释放连接
    • 4. 完整的代码
    • 5. 如何不把 sql 写死 ?
    • 6. 获取连接失败的情况
  • 四. JDBC常用接口和类
    • 1. 数据库连接 Connection
    • 2. Statement 对象
    • 3. PreparedStatement 的预编译

一. 概念

JDBC: 即Java Database Connectivity,java数据库连接。是一种用于执行SQL语句的Java API,
它是Java中的数据库连接规范。这个API由 java.sql.*,javax.sql.* 包中的一些类和接口组成,
它为Java开发人员操作数据库提供了一个标准的API,可以为多种关系数据库提供统一访问。

产生的原因:
MySQL、Oracle、SQL server 这些数据库都提供自己的 API 来支持程序员实现自己的客户端来完成一些增删改查功能, 没有业界统一的标准,Java 当然不乐意了, 因为 Java 诞生就是为了跨平台的。
所以 Java 就搞出了 JDBC,约定一组 API 为 JDBC,里面包含一些类和方法, 通过这些类和方法实现数据库的基本操作,由各厂商提供各自的 “数据库驱动包” 来和 JDBC 的 API 对接。
所以,只要掌握这一套 JDBC API 就可以操作各种数据库了。

二. JDBC 工作原理

JDBC 为多种关系数据库提供了统一访问方式,作为特定厂商数据库访问API的一种高级抽象,它主要包含一些通用的接口类。

JDBC 访问数据库层次结构:
【MySQL】JDBC 编程详解_第1张图片

JDBC 优势:

  • Java 语言访问数据库操作完全面向抽象接口编程
  • 开发数据库应用不用限定在特定数据库厂商的 API
  • 程序的可移植性大大增强

三. JDBC 使用

注意:
JDBC 只支持关系型数据库, 不支持非关系型数据库。

1. 创建项目

创建一个项目即可。

2. 引入依赖

  • 准备数据库驱动包,并添加到项目的依赖中:
    在项目中创建文件夹 lib,并将依赖包(MySQL 使用的是哪个系列就用哪个系列的驱动)如 mysql-connector-java-5.1.47.jar (下载地址:Maven 中央仓库) 复制到 lib 中。
    右键上面的目录,选择 add as library 选项,这样才能把 jar 包导入到项目中,项目才能读取到里面的 .class 文件。

3. 编写代码

(1). 创建数据源

// 创建数据源
DataSource dataSource = new MysqlDataSource();
((MysqlDataSource)dataSource).setURL("jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8&useSSL=false");
((MysqlDataSource)dataSource).setUser("你的用户名");
((MysqlDataSource)dataSource).setPassword("你的数据库密码");

注意:

  1. DataSource 对象描述了数据库对象在哪。

  2. MySQL 数据连接的 URL 参数格式如下:
    jdbc:mysql://服务器地址:端口/数据库名?参数名=参数值&参数名=参数值&…
    服务器地址:我们写 的 127.0.0.1 是因为我们连接的是 本机的数据库, 所以用的是环回 IP 127.0.0.1,如果连接的是服务器上的数据库就得写对应的 IP 地址。
    端口:MySQL 的默认端口号为 3306
    数据库名:代码中的 URL 中的 test 要换为自己的数据库的名字。

  3. setUser、setPassword 都是自己的用户名(MySQL 默认用户为 root )和密码。

  4. 为什么 使用向上转型,DataSource 引用 MysqlDataSource 对象,然后设置参数时 先强转(向下转型)回来,为什么不直接使用 MysqlDataSource 对象,这样不就不用来回转了嘛 ?
    为了使代码低耦合:
    后面代码中若需要用到 DataSource 类型,使用的相关参数也是 dataSource , 未来如果 不适用 MySQL 做数据库了, 使用其他数据库了 如 PostgreSQL 了,代码几乎不用怎么改动。
    但是如果直接写成 :

MysqlDataSource mysqlDataSource = new MysqlDataSource();

这样不需要来回转换, 但是代码中充斥着 MysqlDataSource,到时候一旦更换数据库,代码要改动的地方就非常多了, 基本凉凉。

(2). 建立数据库连接

// 建立连接
Connection connection = dataSource.getConnection(); // 注意处理异常

(3). 创建 SQL

JDBC 中构造的 sql 不需要带上 ; 符号

// 创建 sql

// 更新的 sql 
String sql = "insert into student values ('wangwu', 90)";
PreparedStatement statement = connection.prepareStatement(sql);

// 查询的 sql
// String sql2 = "select * from student"; // 不用加上 ;
// PreparedStatement statement2 = connection.prepareStatement(sql2);

只一个 String 类型的 sql 还不行,需要把这个 String 包装成一个语句对象 PreparedStatement 。

(4). 执行 SQL

// 执行 sql
int ret = statement.executeUpdate();

// 执行查询的 sql 
// ResultSet resultSet = statement2.executeQuery();

执行 增删改 的 sql 使用 executeUpdate 方法, 返回值是受影响的行数。
执行 查询 的 sql 使用 executeQuery 方法,并使用 ResultSet 接收结果。

(5). 遍历结果集

      // 执行 查询的 sql 时需要遍历结果集
      while (resultSet.next()) {
          int age = resultSet.getInt("age");
          String name = resultSet.getString("name");

          System.out.println(" 姓名: " + name + " 年龄: " + age);
      }

可以根据 列名 获取对应的值, 也可以根据列的下标(从 1 开始)获取,但是不推荐。

(6). 释放连接

        // resultSet.close(); // 如果查询的话需要释放
        statement.close(); 
        connection.close();
        // 也要注意处理异常

先释放 statement 再 释放 connection
类似于关冰箱,先把抽屉关了, 再把冰箱门关了。

4. 完整的代码

以查询为例:

    public static void main(String[] args) throws SQLException {
        // 1. 创建数据源
        DataSource dataSource = new MysqlDataSource();
        ((MysqlDataSource)dataSource).setURL("jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8&useSSL=false");
        ((MysqlDataSource)dataSource).setUser("root");
        ((MysqlDataSource)dataSource).setPassword("123456");

        // 2. 建立数据库连接
        Connection connection = dataSource.getConnection(); // 注意处理异常

         // 3. 创建 sql
         String sql = "select * from student"; // 不用加上 ;
         PreparedStatement statement = connection.prepareStatement(sql);

         // 4. 执行查询的 sql
         ResultSet resultSet = statement.executeQuery();

        // 5. 遍历结果集 (查询的话需要)
        while (resultSet.next()) {
            int age = resultSet.getInt("age");
            String name = resultSet.getString("name");

            System.out.println(" 姓名: " + name + " 年龄: " + age);
        }

        // 6. 释放连接
        resultSet.close(); // 如果查询的话需要释放
        statement.close();
        connection.close();
    }

【MySQL】JDBC 编程详解_第2张图片

使用步骤总结:

六个步骤:

  1. 创建数据源: DataSourse
  2. 建立数据库连接: Connection
  3. 创建 sql: PreparedStatement
  4. 执行 sql: executeUpdate/executeQuery
  5. 遍历结果集(如果是 查询的话): ResultSet
  6. 释放连接 ResultSet / PreparedStatement / Connection close()

注意:

整个写代码的时候注意处理异常,要么抛给上层调用者,要么自己处理掉。

5. 如何不把 sql 写死 ?

使用 PreparedStatement 并使用占位符

String name = "zhouba";
int age = 88;
String sql = "insert into student values(?,?)"; // 不用加上 ;

PreparedStatement statement = connection.prepareStatement(sql);
statement.setString(1, name);
statement.setInt(2, age);
statement.executeUpdate();

使用 ? 先占一个位置,再使用 PreparedStatement 的 setXXX 方法进行替换,注意类型要一致。
注意该方法的第一个参数 是 ? 的位置, 下标从 1 开始,第二个参数是要填上去的值。

如果想一次插入多条数据, 就使用多个 ?, 对应替换上去就行了。

String sql = "insert into student values(?,?),(?,?),(?,?),(?,?)"; // 不用加上 ;

不要使用字符串拼接实现动态 sql, 因为会引入 sql 注入问题
比如: 可能我们如果使用 字符串拼接的话, 会像下面这么写

Scanner scanner = new Scanner(System.in);
String name = scanner.nextLine();
String password = scanner.nextLine();
String sql = "select * from user where name=" + name + " and password=" + password;
PreparedStatement statement = connection.prepareStatement(sql);

正常情况下没有什么问题, 但是, 有一个巨大的漏洞,
当用户输入:

'zhangsan'
'' or 1=1

(或者输入一些其他的恶意代码)
拼接成的 sql 为

select * from user where name='zhangsan' and password='' or 1=1

最后出现了一个 or 1=1
这在什么条件下都会成立的,所以说这个 sql 完全能执行成功,
在恶意用户不用知道 正确的用户名和密码的情况下,能查询数据库中的所有内容, 这是相当可怕的,尤其在一些银行等数据库内容非常机密的情况下。
所以, 我们不能使用 字符串拼接来实现动态的 sql ,
而 使用 PreparedStatement 的占位符则不会出现这种情况。

6. 获取连接失败的情况

  • 数据库地址不对
  • 端口号不对
  • 数据库名不对
  • 用户名不对
  • 密码不对
  • 其他情况

四. JDBC常用接口和类

1. 数据库连接 Connection

Connection 接口实现类由数据库提供,获取 Connection 对象通常有两种方式:

  • 一种是通过 DriverManager(驱动管理类)的静态方法获取:
// 加载JDBC驱动程序:反射,这样调用初始化com.mysql.jdbc.Driver类,即将该类加载到JVM方法区,并执行该类的静态方法块、静态属性。
Class.forName("com.mysql.jdbc.Driver");

// 创建数据库连接
Connection connection = DriverManager.getConnection(url,user,password);
  • 一种是通过 DataSource(数据源)对象获取。实际应用中会使用 DataSource 对象。
DataSource ds = new MysqlDataSource();
((MysqlDataSource) ds).setUrl("jdbc:mysql://localhost:3306/test");
((MysqlDataSource) ds).setUser("root");
((MysqlDataSource) ds).setPassword("123456");
Connection connection = ds.getConnection();

以上两种方式的区别是:

  1. DriverManager 类来获取的 Connection 连接,是无法重复利用的,每次使用完以后释放资源时,通过 connection.close() 都是关闭物理连接。
  2. DataSource 提供连接池的支持。连接池在初始化时将创建一定数量的数据库连接,这些连接是可以复用的,每次使用完数据库连接,释放资源调用 connection.close() 都是将 Conncetion 连接对象重新初始化然后回收,不用关闭物理连接。

非常不建议使用 DriverManager :

  1. 使用了反射,非常影响代码的可读性,也不利于 IDEA 对代码解析校验。
  2. DataSourse 内置连接池,在频繁创建/断开连接时,DataSourse 比 DriverManager 的方式更高效。

2. Statement 对象

Statement 对象主要是将SQL语句发送到数据库中。JDBC API 中主要提供了三种 Statement 对象。

【MySQL】JDBC 编程详解_第3张图片

实际开发中最常用的是 PreparedStatement 对象:

  1. 可以参数化 SQL 查询。
  2. 占位符为 ?,下标从 1 开始,占位符不能使用多值。
  3. 使用 SQL 预编译。
  4. 可以阻止常见的 SQL 注入攻击。
  5. 性能比 Statement 高。

PreparedStatement 与 Statement 的区别:

  1. Statement 是 PreparedStatement 的父接口。
  2. 语法不同:PreparedStatement 使用预编译,使用动态 sql 时,相同的 sql 语句,除了参数不同,只需发送一次 sql,后面的只发送了参数,共用一个 sql 语句。(同构)
    而 Statement 只能使用静态 sql。(异构)
  3. 效率不同:PreparedStatement 使用了缓冲区,效率比 Statement 高。
  4. 安全性不同: PreparedStatement 可有效防止 sql 注入,Statement 不能。

3. PreparedStatement 的预编译

  1. 只有数据库服务器支持预编译,JDBC 驱动才能使用数据库预编译功能,预编译在比较新的 JDBC 驱动中默认是关闭的,需要配置才能打开。
  2. PreparedStatement 预编译是数据库进行的,编译后 函数的 key 缓存在 PreparedStatement 中,函数本身 缓存在 数据库服务器中。
  3. 预编译前检查 sql 语法是否正确。
  4. PreparedStatement 需要使用带 占位符的 sql, 如果使用静态 sql, 也还是会编译多次。Statement 本身就只支持静态的 sql。
  5. 数据库服务器对 sql 模板进行编译,且 PreparedStatement 存储了函数 的 key, 所以 PreparedStatement 做的就是把参数转义后直接传参数到数据库服务器中,然后让函数执行,所以 PreparedStatement 能防止 sql 注入。
  6. PreparedStatement 存储的 key 和 数据库存储的函数都建立在数据库连接的基础上,连接断开,key 和函数都清空。
  7. 各个连接之间的预编译都是相互独立的。
  8. Statement 不缓存函数的key, 数据库也不缓存函数, 所以多次执行相同一条 sql 时,还是会先检查 sql 的语法,再编译执行。

好啦,以上就是对 JDBC 编程的讲解,希望能帮到你 !
评论区欢迎指正 !

你可能感兴趣的:(数据库,mysql,数据库)