MyBatis师尊JDBC

2 篇文章 0 订阅
1 篇文章 0 订阅

MyBatis师尊JDBC

概要:

  1. 前言
  2. 重新理解JDBC
  3. MyBatis与JDBC整合

而我们现在已经很少直接使用JDBC了。为了让大家更好的看懂MyBatis源码,很有必要在温故知新一下JDBC。

重新理解JDBC

JDBC全称Java Database Connectivity,它是JAVA操作数据库的一套标准。通过它去操作各数据库。

1.什么是标准

现实中,我们对于标准的理解,通常指统一的尺寸,如室内门的标准是86cm*205cm,要不是这个尺寸那就是非标门如果你家的门是非标门,那就祈祷它不要坏。因为更换非标门,同样品质要出更多的钱,得定制。这就是标准的好处,兼容性高,更要的是可以为你省钱。

为啥我这么清楚?“非标门"、"不要坏" 我家门刚好凑齐。

 2.支持JDBC的后果

如果有数据库就是不服JDBC,不支持其标准,会怎么样?

那我们操作这个数据库就得”定制“代码,直接访问它的驱动。接下来我们演示一下,不用JDBC 怎么操作MySql:

 
1 //1.创建Driver
2 Driver driver = new com.mysql.cj.jdbc.Driver();
3 //2.获取连接
4 JdbcConnection conn= driver.connect(url,properties)
5 //3.预编译SQL
6 JdbcPreparedStatement sta=conn.prepareStatement(sql);
7 //4.执行SQL
8 sta.executeQuery()

写这种代码,不仅要去学习它的文档,还加大了编码量。所以如果有客户让我们写这种代码,那得加钱。

所以JDBC对于JAVA,地位就像空气对于我们一样,如果有关系数据库厂商 不支持它,都懒的去看它,更别说用了。后果是什么你懂的。

 

3.JDBC的实现

前面知道JDBC的历害,不支持它后果很严得,它具体是如何执行的呢?估计有些同学会认为是, 我们调用jdbc,jdbc在调用各数据库驱动。

 

这是错误的,jdbc并没有起到代理的作用,他做的是引导。事实上除了DriverManager之外,大部分它都是接口。 这些接口由各数据库提供的Jar包(驱动包)实现,比如在MySql中,Connection的实现是com.mysql.cj.jdbc.ConnectionImpl。

 

JDBC唯一要要做就是,在DrvierManager.getConnection() 时引导对应对应Driver来创建连接 。引导过程分为两步:

  1. 注册Driver实例
  2. 基于URL匹配对应Driver实例,然后创建连接。

注册Driver实例

DriverManager.registeredDrivers 中存储了,各数据库Driver实例。这些实例通常都是自己注册进去的。谁来触发这个注册动作呢?就是下面这行代码,熟悉吧

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

我们刚学习JDBC时,总是对这行代码不解,以为是把这个类加载到ClassLoader当中去。其实它更实际意义是:通过静态块,注册自己实例到DriverManager。

 
1 package com.mysql.cj.jdbc
2 public class Driver  {
3  static {
4          java.sql.DriverManager.registerDriver(new Driver());
5   }
6 }

SPI 装载

随着SPI机制的引入,我们已经不需要在手动Class.forName了。在DriverManager初始化时,会利用SPI中的 ServiceLoader 装载所有Driver类。依据是 xxx.jar/META-INF/services/java.sql.Driver文件,该文件指明了要加载Driver类。一但该类加载,上述静态块中注册逻辑就会执行。

说明:xxx.jar代指数据库驱动包,如mysql-connector-java-8.0.17.jar

以下为DriverManager利用SPI加载Driver的代码:

1 ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
2 Iterator<Driver> driversIterator = loadedDrivers.iterator();
3  while(driversIterator.hasNext()) {
4    driversIterator.next();
5 }

完整代码详见:java.sql.DriverManager.loadInitialDrivers()

创建连接

连接都由Driver.connect() 创建,DriverManager遍历所有Driver实例,直到有Driver 能基于URL创建出connect为止。

1  Connection getConnection(
2      String url, Properties info, Class<?> caller)  {
3    
4   for(DriverInfo aDriver : registeredDrivers) {
5               Connection con = aDriver.driver.connect(url, info);
6                if (con != null) {
7                   return (con);
8                 }
9    }
10 }

注:篇幅所限只展现了关键源码,完整代码详见:java.sql.DriverManager#getConnection()

创建连接后,接着声明Statement对象,但这时不在需要JDBC介入了,因为Connection 是驱动包自己实现。

核心流程

注:以下为JDBC标准流程和API讲述,如果你已非常熟悉可跳过,直接看MyBatis整合部分。

JDBC标准流程分为以下步骤:

  1. 建立连接 (Connection)
  2. 创建声明(Statement)
  3. 执行SQL获取结果集(ResultSet)
  4. 处理结果集
  5. 关闭资源

1.获取连接

通过DriverManager的getConnection来创建一个连接,传入url、user、password等参数。

1 Connection connection = DriverManager
2 .getConnection(url, user, password);

关于Connection底层实现,有几点大家可以简单知晓下:

  1. 连接的创建,最终是数据驱动创建的,DriverManager只是引导。
  2. 连接指和数据库建立远程通信,使用Socket进行字节传输,并有一套自己的报文协议。
  3. 连接是不可以跨线程,并发使用的,会导致传输数据和状态混乱。

关于连接实现,不同数据库驱动实现方式都不一样,不好展开细讲,

感兴趣的可以去研究一下MySql驱动。 建议从协议报文开始

2.创建Statement

Statement用于执行具体的SQL,并返回结果集。他有三种类型:

  1. Statement(基础):执行静态Sql、批处理、设置加载行数
  2. PreparedStatement(预处理):预编译参数,可防Sql注入
  3. CallableStatement(存储过程):支持对存储过程调用,基于出参,可处理多份结果集三者是如下图表示的继承关系,即后者拥有前者的所有功能。

 

结果集参数

Statement 由Connection 创建,创建方法相信大家都清楚,这里介绍一下创建过程中的三个参数、它们都跟结果集相关:

参数

描述

resultSetType

结果集是否可前后滚动

TYPE_FORWARD_ONLY

结果集的读取只能步步向前,不可前后滚动

TYPE_SCROLL_INSENSITIVE

可前后滚动,并且对修改不敏感

TYPE_SCROLL_SENSITIVE

可前后滚动,对修改敏感

resultSetConcurrency

结果集是否可修改

CONCUR_READ_ONLY

只读结果集

CONCUR_UPDATABLE

可读写结果集,即可通过结果集修改表数据

resultSetHoldability

结果集保持方式

HOLD_CURSORS_OVER_COMMIT

在连接提交后,结果集仍然可用

CLOSE_CURSORS_AT_COMMIT

在提交或回滚后,自动关闭结果集

注:表中常量均来自 java.sql.ResultSet

3.执行Sql

指发送Sql语句到数据库,并返回结果集。相关可以分成三类:

1.查询:

执行 executeQuery立即返回结果集,如果是execute需要在调用getResultSet()获取结果集。 

2.修改

执行executeUpdate返回影响行,通过execute执行,需要在调用getUpdateCount()获取。

注:修改代指:增、删、改

3.批处理

批处理分为两步。

  • 第一步:准备,Statement.addBatch(sql) 添加一个静态SQl,PreparedStatement.addBatch() 添加一个预处理SQL。
  • 第二步:执行,通过executeBatch() 一次性发送出去,然后获取执行结果。

注意:批处理只针对修改操作。

4.结果处理

执行完SQL后,其结果会放置于内存当中,通过ResultSet 即可获取。这数据分为两类,

第一类:元数据,包含返回的列数量、列名称、列类型。通过getMetaData()获取。

第二类:具体结果,它是一个二维数据,通过next()依次读取行,在getXXX 获取当前行中的列。

1 while (rs.next()) {
2    String name = rs.getString("name");
3    // ... 
4 }

5.释放资源

业务处理完毕之后我们需要及时关闭Statement和Connection。

1 rs.close();
2 stmt.close();
3 connection.close();

MyBatis整合

前面讲述了JDBC和各数据库驱动的关系,一个定义标准,一个实现标准。接下来我们在回到MyBatis。它是如何使用JDBC的?关键代码在哪里?

其对JDBC的调用分布在四大组件上:

  1. 执行器:操作Connection 进行连接、提交、回滚、关闭。
  2. Sql处理器:创建Sql声明(Statement)、设置超时、设置FetchSize、关闭Statement
  3. 参数处理器:为预处理器(PreparedStatement)设置参数
  4. 结果集处理器:读取结果集(ResultSet)、关闭结果集。

可以看出,MyBatis在对JDBC使用上,每个组件分工非常明确。各自都处理JDBC上功能。前期你对MyBatis组件比较陌生,可以对照JDBC组件进行记忆。

总结:

  1. JDBC是一套标准,好处可降低学习成本,减少编码量。
  2. 各数据库是这套标准的具体实现。
  3. 在执行层面,JDBC唯一作用是引导,而非代理。
  4. JDBC 基于DriverManger来指定驱动创建连接,前提是驱要自己先注册进去。
  5. MyBatis在四在核心组件均有操作JDBC,并且分工明确。

 

最后的讨论:

MyBatis、JDBC、数据库驱动三者是什么关系?你能用现实世界中的例子表述出来吗?

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值