JDBC

发布时间 2023-09-08 20:49:45作者: yanggdgg

JDBC

一、 JDBC简介

JDBC就是由sun公司定义的一套操作所有关系型数据库的规则(接口), 而数据库厂商需要实现这套接口, 厂商的实现类在引入的数据库驱动jar包中。

  • JDBC(Java Data Base Connectivity),是 Java连接数据库的技术。

  • 是一种执行SQL的API, 可以为多种关系型数据库提供统一的访问。

  • 它是由一组用java语言编写的类和接口组成, 是Java访问数据库的标准规范。

1. 执行原理

数据库驱动程序

  • 是直接操作数据库的一个程序;
  • 不同数据库的驱动名字有差异;
  • 在程序中需要依赖数据库驱动来完成对数据库的操作。

二、 JDBC实现增删改查

使用JDBC要先引入数据库驱动包

  • JDBC规范中的驱动接口: java.sql.Driver

  • MySQL驱动包提供的实现类: com.mysql.cj.jdbc.Driver

1. 查询

1.1 流程

① 加载(注册)驱动

Class.forName(数据库启动实现类)

作用:触发类加载,执行静态代码块

Driver类中的静态代码块如下:

 就是将Driver对象交给DriverManager管理了。

DriverManager是驱动管理类,可以通过驱动管理类获取到数据库的连接

注意: JDBC4.0之后, 在每个驱动jar包中META-INF/services目录下提供了一个名为java.sql.Driver的文件。

② 获取连接

JDBC提供了Connection接口,代表一个数据库连接接口。

MySQL通过DriverManager类中的静态方法 getConnection()获取连接。

Connection getConnection(String url, String user, String password)

3个参数的详细说明:

url:连接数据库的地址,MySQL的格式为:jdbc:mysql://ip地址:端口号/数据库名

user:数据库用户名

password:数据库密码

url组成详细说明:

String url="jdbc:mysql://localhost:3306/sygg?
useSSL=false&useUnicode=true
&characterEncoding=utf-8&&serverTimezone=Asia/Shanghai";

JDBC规定url的格式由三部分组成,每个部分中间使用冒号分隔

第一部分是协议 jdbc,这是固定的

第二部分是子协议,就是数据库名称,连接mysql数据库,第二部分当然是mysql了

第三部分是由数据库厂商规定的,我们需要了解每个数据库厂商的要求,mysql的第三部分分别由数据库服务器的IP地址(localhost)、端口号(3306),以及要使用的数据库名称组成

 ③ 获取SQL执行器

通过Connection的 createStatement()方法获取sql语句执行对象

Statement createStatement()

④ 发送SQL,数据库执行SQL,获取返回结果

Statement对象用于发送SQL语句给MySQL服务器,执行SQL 语句并返回它所生成结果。

Statement类常用方法说明
int executeUpdate(String sql) 执行insert、delete、update语句,返回int类型,代表受影响的函数
ResultSet executeQuery(String sql) 执行select语句,返回ResultSet结果集对象

⑤ 处理结果集

查询后的结果会存放到ResultSet结果集中。

ResultSet接口方法说明
boolean next()

1.游标向下一行

2.返回boolean类型,如果有下一条记录,返回true,否则返回false

xxx getXxx(String or int)

1.通过列名获取数据

2.通过列索引获取数据,索引从1开始

ResultSet特点

  • ResultSet 对象具有指向其当前数据行的指针。最初,指针被置于第一行之前。next 方法将指针移动到下一行;因为该方法在 ResultSet 对象中没有下一行时返回 false,所以可以在 while 循环中使用它来迭代结果集。

  • 默认的 ResultSet 对象仅有一个向前移动的指针。因此,只能迭代它一次,并且只能按从第一行到最后一行的顺序进行。

原理

⑥ 释放资源

最后释放相关的资源即可:

  • 需要释放的对象:ResultSet结果集,Statement语句,Connection连接

  • ResultSet结果集: 当它的 Statement 关闭、重新执行或用于从多结果序列中获取下一个结果时,该ResultSet将被自动关闭

  • 释放原则: 先开的后关, 后开的先关 Statement ==> Connection

 //6. 释放资源
        statement.close();
        connection.close();

2. 增、删、改

和查询流程类似(比查询少了个处理结果集的步骤)

需要注意的是增删改调用了statement.executeUpdate(sql),而查询时调用的是executeQuery(sql)方法

另外,增删改执行方法后返回的是int类型,即返回影响的行数,而查询返回的查询到的记录(ResultSet结果集)。

三、ORM

ORM(Object Relational Mapping,简称ORM,或O/R mapping)对象关系映射

是一种为了解决面向对象语言与关系数据库存在的互不匹配的现象。

即数据库中一张表对应java的一个类。

实体类

实体类就是一个定义了属性,拥有getter、setter、无参构造方法(基本必备)的一个类。实体类可以在数据传输过程中对数据进行封装,相当于一个“工具”、“容器”、“载体”,能存储、传输数据,能管理数据。

四、 PreparedStatement预处理对象

通过PreparedStatement可以解决SQL注入的问题。

SQL注入

SQL注入就是 手动输入的内容作为SQL语句的一部分,但改变了SQL语句的本意

1. PreparedStatement接口

  • PreparedStatement 是Statement 接口的子接口,继承于父接口中所有的方法。它是一个预编译的 SQL 语句对象。

  • 预编译:是指SQL 语句被预编译, 并存储在PreparedStatement 对象中。然后可以使用此对象多次高效地执行该语句。

2. 特点

  • 因为有预先编译的功能,提高SQL的执行效率

  • 可以有效的防止SQL 注入的问题,安全性更高

3. 获取PreparedStatement对象

通过Connection创建PreparedStatement对象。

Connection接口中的方法说明
PreparedStatement prepareStatement(String sql) 指定预编译的SQL语句,SQL语句中使用占位符?创建一个语句对象

4. 常用方法

常用方法说明
int executeUpdate() 执行insert、delete、update语句
ResultSet executeQuery() 执行select语句,返回ResultSet结果集对象

5. 如何使用

① 编写SQL语句,未知内容用占位符 ? 代替

② 调用connection.prepareStatement(sql)方法获取PreparedStatement对象

③ 设置实际参数,即给占位符 ? 赋值:setXX(占位符的位置,真实的值) 

  注意:占位符的位置下标从1开始。

④ 执行SQL

6. 执行原理

Statement会在执行SQL时才进行解析、预处理,效率慢且存在SQL注入问题

而PreparedStatement在执行SQL前就会进行解析、预处理,语义已经确定不会再被改变,给占位符赋的值仅仅被当做一个值而不是SQL语句的一部分。

另外,statement执行多条SQL就会多次进行解析、预处理,而PreparedStatement只进行一次解析、预处理。

7. 批处理

PreparedStatement中的addBatch()方法:添加到批量操作

可以用于批量的写操作(增删改,主要用于添加数据)。

JDBC提供的批处理功能(Batch)。关键点如下:

  1. 批处理是指将关联的SQL语句组合成一个批处理,并将他们当成一次调用提交给数据库, 一次发送多个SQL语句到数据库,可以减少通信的资源消耗,从而提高了性能

  2. executeBatch() 方法用于启动执行所有组合在一起的语句。

  3. executeBatch() 方法返回一个整数数组,数组中的每个元素代表了各自的影响行数

 对于多次写操作,批处理又进一步提升了效率。

五、 JDBC事务控制

1. 事务概述

事务(ACID): 原子性、一致性、隔离性、持久性。 ​ 事务的隔离级别:

  1. 读未提交: 脏读 不可重复读 幻读

  2. 读已提交: 不可重复读 幻读

  3. 可重复读: 幻读

  4. 串行化: 完全解决,整张表加锁, 同时只能有一个客户端操作。

事务是一个整体, 由一条或者多条SQL语句组成, 这些SQL语句要么都执行成功, 要么就失败, 只要有一条SQL出现异常, 整个操作就会回滚。

回滚: 就是事务运行的过程中发生了某种故障, 或者SQL出现了异常, 事务不能继续执行, 系统将事务中对数据库的所有已完成的操作全部取消, 回滚到事务开始时的状态。

宕机等情况自动回滚, 代码的bug手动回滚。

事务控制的使用方式:

  1. 使用MySQL的命令来操作事务控制

  2. 使用JDBC操作事务控制

2. 事务相关API

通过connection中的方法实现事务控制

方法说明
void setAutoCommit(boolean autoCommit) 参数设置为true,表示自动提交; 设置为false,表示手动提交
void commit() 提交事务
void rollback() 回滚事务

rollback回滚事务在catch代码块中执行,即出现错误时及时回滚事务。

最后在finally代码块中释放资源。

六、 连接池

  数据库连接池的基本原理是连接池对象中维护一定数量的数据库连接,并对外暴露数据库连接获取和返回方法

 1. 连接池工具类

  1. C3P0数据库连接池,速度相对较慢,稳定性不错。(hibernate,spring)

  2. DBCP数据库连接池,速度相比C3P0快,但不稳定。

  3. Druid(德鲁伊)数据库连接池,是由阿里提供的连接池,及DBCP,C3P0,...优点于一身的数据库连接池。

1.1 C3P0使用

//创建一个数据源对象
ComboPooledDataSource cpds = new ComboPooledDataSource();
String url = "jdbc:mysql://localhost:端口号/数据库名?useSSL=false&useUnicode=true" +
    "&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
String driver = "com.mysql.cj.jdbc.Driver";
String username = "数据库用户名";
String password = "数据库密码";
cpds.setDriverClass(driver);
cpds.setJdbcUrl(url);
cpds.setUser(username);
cpds.setPassword(password);
//设置初始化连接数
cpds.setInitialPoolSize(10);
//最大连接数
cpds.setMaxPoolSize(50);
//测试连接池的效率, 测试对 mysql 5000 次操作
long start = System.currentTimeMillis();
for (int i = 0; i < 5000; i++) {
    Connection connection = cpds.getConnection();
    connection.close();
}
long end = System.currentTimeMillis();
System.out.println("c3p0 5000 连接 mysql 耗时=" + (end - start));

1.2 Druid使用

DruidDataSource dbs = new DruidDataSource();
String url = "jdbc:mysql://localhost:端口号/数据库名?useSSL=false&useUnicode=true" +
    "&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
String driver = "com.mysql.cj.jdbc.Driver";
String username = "数据库用户名";
String password = "数据库密码";
dbs.setDriverClassName(driver);
dbs.setUrl(url);
dbs.setUsername(username);
dbs.setPassword(password);
//测试连接池的效率, 测试对 mysql 5000 次操作
long start = System.currentTimeMillis();
for (int i = 0; i < 500000; i++) {
    Connection connection = dbs.getConnection();
    connection.close();
}
long end = System.currentTimeMillis();
System.out.println("druid 5000 连接 mysql 耗时=" + (end - start));