【源码】Sharding-JDBC源码分析之JDBC
Sharding-JDBC系列
1、Sharding-JDBC分库分表的基本使用
2、Sharding-JDBC分库分表之SpringBoot分片策略
3、Sharding-JDBC分库分表之SpringBoot主从配置
4、SpringBoot集成Sharding-JDBC-5.3.0分库分表
5、SpringBoot集成Sharding-JDBC-5.3.0实现按月动态建表分表
6、【源码】Sharding-JDBC源码分析之JDBC
前言
《Sharding-JDBC系列》的前5篇博文分析了Sharding-JDBC的使用,本篇带大家从源码的角度整体的熟悉Sharding-JDBC的实现原理。从Sharding-JDBC 5.1.2 版本开始,提供了原生JDBC驱动ShardingSphereDriver。
JDBC
2.1 JDBC概述
JDBC(Java DataBase Connectivity),是java连接数据库操作的原生接口API,为开发者访问数据库提供标准的接口。各数据库厂商依照JDBC规范,实现规范中的接口,实现数据库的连接。Java开发者使用同样的访问代码,配置不同的Driver、url以及账号,即可实现不同数据库厂家的数据库连接。
2.2 JDBC组成
JDBC是由一组用Java语言编写的类和接口,主要有DriverManager、Driver、Connection、Statement、ResultSet等。
使用JDBC连接数据库的步骤包含以下步骤:
1)加载JDBC驱动程序:使用Class.forName()方法加载特定数据库的JDBC驱动程序,即对应数据库插件中实现Driver接口的类;
如:
Mysql8之前为com.mysql.jdbc.Driver
Mysql8的com.mysql.cj.jdbc.Driver
Oracle的oracle.jdbc.driver.OracleDriver
2)创建Connection数据库连接:使用DriverManager.getConnection()方法返回一个Connection,创建一个数据库连接对象。该方法需要指定数据库的连接字符串、用户名和密码;
3)创建Statement对象:通过数据库连接对象Connection,创建一个Statement对象,用于执行SQL语句;
也可以通过数据库连接对象Connection,预编译SQL语句,获得一个PreparedStatement对象。该方式通常与动态SQL语句结合使用,获得PreparedStatement对象后,给动态SQL语句中的占位符赋值。使用该方式不会有SQL注入的风险。
4)执行SQL语句:使用Statement对象执行SQL语句,包括插入、查询、更新和删除等操作。如果是查询操作,返回ResultSet对象;
5)关闭数据库连接:使用数据库连接对象Connection的close()方法关闭数据库连接;
2.3 JDBC使用
以下以MySQL为例,讲解一下JDBC的使用。
2.3.1 使用创建Statement的方式
@SpringBootTest
public class Test {@org.junit.jupiter.api.Testpublic void mysqlTest() throws Exception {// 1、加载启动Class.forName("com.mysql.cj.jdbc.Driver");String url = "jdbc:mysql://localhost:3306/test";String username = "root";String password = "123456";// 2、获取Connection对象Connection connection = DriverManager.getConnection(url, username, password);String sql = "select * from tb_user";// 3、创建Statement对象Statement statement = connection.createStatement();// 4、执行Sql。查询语句返回ResultSetResultSet resultSet = statement.executeQuery(sql);// 5、处理结果集while(resultSet.next()) {System.out.println("name : " + resultSet.getString("name"));}// 6、关闭数据库连接statement.close();connection.close();}}
2.3.2 使用PreparedStatement的方式
@org.junit.jupiter.api.Testpublic void mysqlTest2() throws Exception {// 1、加载启动Class.forName("com.mysql.cj.jdbc.Driver");String url = "jdbc:mysql://localhost:3306/test";String username = "root";String password = "123456";// 2、获取Connection对象Connection connection = DriverManager.getConnection(url, username, password);String sql = "insert into tb_user(name, state) values(?, ?)";// 3、预编译SQL语句,没有执行SQL// 此处传入两个参数值,第二个参数为Statement.RETURN_GENERATED_KEYS,指定返回数据库生成的主键PreparedStatement statement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);// 占位符赋值statement.setString(1, "王五");statement.setString(2, "1");// 4、执行Sql,此处不需要再传入SQL语句。执行更新操作,返回操作成功的记录数int rs = statement.executeUpdate();// 5、获取生成的主键ResultSet generatedKeys = statement.getGeneratedKeys();// 移到结果集的下一行generatedKeys.next();System.out.println("执行结果 : " + rs + ",记录主键:" + generatedKeys.getInt(1));// 6、关闭数据库连接statement.close();connection.close();}
1)通过Connection的prepareStatement()方法,可以在第二个参数中指定Statement.RRTURN_GENERATED_KEYS,获取数据库表中设置的自动生成的主键值;
2)可以通过PreparedStatement的addBatch()方法,实现对数据库的批量操作;
2.4 JDBC原理
2.4.1 JDBC的驱动程序
JDBC的驱动程序为实现了java.sql.Driver接口的类。如com.mysql.cj.jdbc.Driver。当使用Class.forName()将对应的驱动类加载到元空间后,会执行类的static静态代码段。源码如下:
package com.mysql.cj.jdbc;import java.sql.DriverManager;
import java.sql.SQLException;public class Driver extends NonRegisteringDriver implements java.sql.Driver {public Driver() throws SQLException {}static {try {DriverManager.registerDriver(new Driver());} catch (SQLException var1) {throw new RuntimeException("Can't register driver!");}}
}
在static静态代码段中,new一个Driver,并通过DriverManager.registerDriver(),将Driver注册到DriverManager中。DriverManager中使用一个静态的线程安全的List常量,保存进一步封装的Driver对象。
package java.sql;public class DriverManager {// 线程安全的静态集合常量,存放Driverprivate final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();public static synchronized void registerDriver(java.sql.Driver driver)throws SQLException {registerDriver(driver, null);}public static synchronized void registerDriver(java.sql.Driver driver,DriverAction da)throws SQLException {// 将driver封装成DriverInfo,保存到registeredDrivers集合中if(driver != null) {registeredDrivers.addIfAbsent(new DriverInfo(driver, da));} else {// This is for compatibility with the original DriverManagerthrow new NullPointerException();}println("registerDriver: " + driver);}
}class DriverInfo {final Driver driver;DriverAction da;DriverInfo(Driver driver, DriverAction action) {this.driver = driver;da = action;}@Overridepublic boolean equals(Object other) {return (other instanceof DriverInfo)&& this.driver == ((DriverInfo) other).driver;}@Overridepublic int hashCode() {return driver.hashCode();}@Overridepublic String toString() {return ("driver[className=" + driver + "]");}DriverAction action() {return da;}
}
Driver中的主要方法:
1)boolean acceptsURL(String url):查询驱动程序是否认为它可以打开到给定url的连接;
Driver制定自己能够连接的url的协议,只有符合自己协议形式的url,返回true;反之,返回false。
2)Connection connect(String url, Properties info):试图创建一个到给定url的数据库连接;
2.4.2 创建Connection数据库连接
通过DriverManager的getConnection()方法,获取一个Connection对象。源码如下:
package java.sql;public class DriverManager {// Driver集合private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();@CallerSensitivepublic static Connection getConnection(String url,String user, String password) throws SQLException {java.util.Properties info = new java.util.Properties();if (user != null) {info.put("user", user);}if (password != null) {info.put("password", password);}return (getConnection(url, info, Reflection.getCallerClass()));}/*** 公共的获取Connection的方法*/private static Connection getConnection(String url, java.util.Properties info, Class<?> caller) throws SQLException {/** 当callerCl为null时,我们应该检查应用程序的(间接调用此类的)类加载器,* 以便可以从这里加载rt.jar之外的JDBC驱动程序类*/ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;synchronized(DriverManager.class) {// synchronize loading of the correct classloader.if (callerCL == null) {callerCL = Thread.currentThread().getContextClassLoader();}}if(url == null) {throw new SQLException("The url cannot be null", "08001");}println("DriverManager.getConnection(\"" + url + "\")");// 浏览已加载的Driver,尝试建立连接。记录引发的第一个异常SQLException reason = null;// 遍历Driverfor(DriverInfo aDriver : registeredDrivers) {// 如果权限不足,跳过if(isDriverAllowed(aDriver.driver, callerCL)) {try {// 建立连接println(" trying " + aDriver.driver.getClass().getName());Connection con = aDriver.driver.connect(url, info);// 连接成功,则返回if (con != null) {// Success!println("getConnection returning " + aDriver.driver.getClass().getName());return (con);}} catch (SQLException ex) {if (reason == null) {reason = ex;}}} else {println(" skipping: " + aDriver.getClass().getName());}}// 如果异常,则抛出if (reason != null) {println("getConnection failed: " + reason);throw reason;}println("getConnection: no suitable driver found for "+ url);throw new SQLException("No suitable driver found for "+ url, "08001");}}
使用注册的Driver,以及传入的url、用户信息,尝试连接数据库,获取一个Connection对象。
2.5 JDBC小结
JDBC通过DriverManager管理Driver,并提供了Driver、Connection、Statement、PreparedStatement、ResultSet等API接口,不同的数据库提供商实现对应接口,实现对数据库的连接、SQL语句的执行等。
ShardingSphereDriver
SpringBoot集成Sharding-JDBC-5.3.0分库分表-CSDN博客
在这篇博文介绍了Sharding-JDBC-5.3.0分库分表的基本使用。示例的application.yml配置信息为:
server:port: 8080
spring:main:# 处理连接池冲突allow-bean-definition-overriding: truedatasource:driver-class-name: org.apache.shardingsphere.driver.ShardingSphereDriverurl: jdbc:shardingsphere:classpath:sharding.yml
即对应的Driver为ShardingSphereDriver,url指向shardingsphere的配置文件sharding.yml。
ShardingSphereDriver的源码如下:
package org.apache.shardingsphere.driver;public final class ShardingSphereDriver implements Driver {private static final int MAJOR_DRIVER_VERSION = 5;private static final int MINOR_DRIVER_VERSION = 1;// 数据库连接缓存private final DriverDataSourceCache dataSourceCache = new DriverDataSourceCache();static {try {// 注册到DriverManagerDriverManager.registerDriver(new ShardingSphereDriver());} catch (final SQLException ex) {throw new DriverRegisterException(ex);}}/*** 在hikari的DriverDataSource或其他连接池中,会通过DriverManager.getDriver(url)获得该ShardingSphereDriver,* 然后调用ShardingSphereDriver.connect()获得数据库连接*/@Overridepublic Connection connect(final String url, final Properties info) throws SQLException {return acceptsURL(url) ? dataSourceCache.get(url).getConnection() : null;}/*** ShardingSphereDriver的url中必须是以jdbc:shardingsphere:开头* @param url* @return*/@Overridepublic boolean acceptsURL(final String url) {return null != url && url.startsWith("jdbc:shardingsphere:");}@Overridepublic DriverPropertyInfo[] getPropertyInfo(final String url, final Properties info) {return new DriverPropertyInfo[0];}@Overridepublic int getMajorVersion() {return MAJOR_DRIVER_VERSION;}@Overridepublic int getMinorVersion() {return MINOR_DRIVER_VERSION;}/*** 不符合JDBC标准* @return*/@Overridepublic boolean jdbcCompliant() {return false;}@Overridepublic Logger getParentLogger() {return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);}
}
1)在static静态代码段中,new一个ShardingSphereDriver,注册到DriverManager中;
2)在acceptsURL(String url)方法中,判断url是否以jdbc:shardingshpere:开头;
3)在connect()方法中,从dataSourceCache中,根据url,获取一个Connection对象;
ShardingSphere中的JDBC
在ShardingSphere框架中,对于JDBC中提供的Driver、Connection、Statement等API接口进行了实现,实现类分别为:
1)Driver:ShardingSphereDriver
2)DataSource:ShardingSphereDataSource
3)Connection:ShardingSphereConnection
4)Statement:ShardingSphereStatement
5)PreparedStatement:ShardingSpherePreparedStatement
小结
限于篇幅,本篇先分享到这里。
由于ShardingSphere采用了原生JDBC的驱动,所以本篇重点讲解了JDBC及其原理。在此基础上,结合ShardingSphereDriver的源码,可以很清晰的对ShardingSphere的框架有一个整体的认识。
ShardingSphere框架就是通过实现JDBC中的接口,在调用真实数据库的Statement执行SQL之前,对SQL语句进行拦截解析,结合分库分表规则,执行对应真实数据库的SQL操作。
关于本篇内容你有什么自己的想法或独到见解,欢迎在评论区一起交流探讨下吧。
相关文章:

【源码】Sharding-JDBC源码分析之JDBC
Sharding-JDBC系列 1、Sharding-JDBC分库分表的基本使用 2、Sharding-JDBC分库分表之SpringBoot分片策略 3、Sharding-JDBC分库分表之SpringBoot主从配置 4、SpringBoot集成Sharding-JDBC-5.3.0分库分表 5、SpringBoot集成Sharding-JDBC-5.3.0实现按月动态建表分表 6、【…...

Java | Leetcode Java题解之第273题整数转换英文表示
题目: 题解: class Solution {String[] singles {"", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine"};String[] t…...

数据结构之深入理解简单选择排序:原理、实现与示例(C,C++)
文章目录 一、简单选择排序原理二、C/C代码实现总结: 在计算机科学中,排序算法是一种非常基础且重要的算法。简单选择排序(Selection Sort)作为其中的一种,因其实现简单、易于理解而受到许多初学者的喜爱。本文将详细介…...

使用vscode搜索打开的文件夹下的文件
右键空白处打开命令面板 摁一次删除键,删除掉图中的大于号 这样就能够找到例化的模块,文件具体在哪个位置,然后打开了...
力扣778.水位上升的泳池中游泳
力扣778.水位上升的泳池中游泳 二分 bfs class Solution {int dx[4] {1,0,-1,0},dy[4] {0,1,0,-1};public:int swimInWater(vector<vector<int>>& grid) {int n grid.size();auto check [&](int mid) -> bool{queue<pair<int,int>>…...

Nacos-2.4.0最新版本docker镜像,本人亲自制作,部署十分方便,兼容postgresql最新版本17和16,奉献给大家了
基于Postgresql数据库存储的nacos最新版本2.4.0,采用docker镜像安装方式 因业务需要,为了让nacos支持postgresql,特意花了两天时间修改了源码,然后制作了docker镜像,如果你也在找支持postgresql的nacos最新版本,恭喜你,你来的正好~ nacos-2.4.0 postgresql的数据库脚本…...

Halcon机器视觉15种缺陷检测案例_9找出所有网格顶点的位置
Halcon机器视觉15种缺陷检测案例_9找出所有网格顶点的位置 效果 原图 代码 *9找出所有网格顶点的位置 dev_update_off ()read_image (Image, 9找出所有风格顶点的位置) get_image_size (Image, Width, Height) *关闭已打开的窗口 dev_close_window ()dev_open_window (0, 0, …...

w30-python02-pytest入门
代码如下: import pytest class Test_Obj:"""测试类"""#用例级别前后置def setup(self):print(用例级别------的前置处理)def teardown(self):print("用例级别--------的后置处理")# 用例def test_case1(self):print(&quo…...

WPF+Mvvm项目入门完整教程-仓储管理系统(二)
目录 一、搭建一个主界面框架二、实现步骤1.主界面区域划分2.主界面区域实现 一、搭建一个主界面框架 主要实现主界面的框架样式和基础功能。这里特别说明一下,由于MvvmLight 已经过时不在维护,本项目决定将MvvmLight框架变更为 CommunityToolkit.Mvvm …...

SkyWalking入门搭建【apache-skywalking-apm-10.0.0】
Java学习文档 视频讲解 文章目录 一、准备二、服务启动2-1、Nacos启动2-2、SkyWalking服务端启动2-3、SkyWalking控制台启动2-4、自定义服务接入 SkyWalking 三、常用监控3-1、服务请求通过率3-2、服务请求拓扑图3-3、链路 四、日志配置五、性能剖析六、数据持久化6-1、MySQL持…...
exo项目目录架构
目录 .yml 文件是 YAML(YAML Aint Markup Language) exo项目目录架构 文件作用 topology、viz:项目拓扑结构可视化相关的代码或工具。 项目目录架构 文件作用 .yml 文件是 YAML(YAML Aint Markup Language) 文件的扩展名,YAML 是一种人类可读的数据序列化标准,通…...
mysql中where与on区别
WHERE子句 作用范围:WHERE子句主要用于过滤FROM子句返回的结果集。它可以在SELECT、UPDATE、DELETE语句中使用,以限制哪些行被包含在最终的查询结果中,或者哪些行被更新或删除。应用场景:当需要基于某些条件过滤结果集时…...
filebeat把日志文件上传到Es中配置(ES7版本)
默认的filebeat配置会把所有的索引都放到一个文件中,通过摸索发现可以自定义索引的名字、模板、生命周期 (重点注意)该配置文件只适应于ES版本是7,不适应于8的版本,两个版本的配置文件差异很大 /app/logs/info.log日…...

Vue Router基础
Router 的作用是在单页应用(SPA)中将浏览器的URL和用户看到的内容绑定起来。当用户在浏览不同页面时,URL会随之更新,但页面不需要从服务器重新加载。 1 Router 基础 RouterView RouterView 用于渲染当前URL路径对应的路由组件。…...

Apache压测工具ab(Apache Bench)工具的下载安装和使用示例
场景 Jmeter进行http接口压力测试: Jmeter进行http接口压力测试_接口压测两万量-CSDN博客 上面讲压测工具Jmeter的使用,下面介绍另外一个ab(Apache Bench)压测工具的使用。 apache bench apache bench是apache自带的压力测试工具。 ab不仅可以对ap…...
IPIDEA与Python爬虫:联手解锁全球电商数据宝库
IPIDEA与Python爬虫:联手解锁全球电商数据宝库 如何运用代理IP在电商领域进行高效数据采集。特别是在遭遇访问限制的情况下,如何优雅地绕过那些恼人的访问管理机制。当然,在我们的探险之旅中,开源神器PlugLink也将适时出场&#…...

Fine-BI学习笔记
官方学习文档:快速入门指南- FineBI帮助文档 FineBI帮助文档 (fanruan.com) 1.零基础入门 1.1 功能简介 完成四个流程:新建分析主题、添加数据、分析数据、分享协作。 示例数据获取:5分钟上手FineBI - FineBI帮助文档 (fanruan.com) 1.2 …...
AI 辅助编程 Coding AI 辅助研发组织的技术蓝图
简简单单 Online zuozuo:欢迎商业合作 简简单单 Online zuozuo 简简单单 Online zuozuo 简简单单 Online zuozuo 简简单单 Online zuozuo :本心、输入输出、结果 简简单单 Online zuozuo :联系我们:VX :tja6288 / EMAIL: 347969164@qq.com 文章目录 AI 辅助编程 Coding A…...

VScode 批量操作
VScode 批量操作 批量修改 按住 alt/option 键, 选择需要批量操作的位置 如果是多行,则按住 altshift 键 可以直接操作 但是有时候比如变量命名,可能需要递增操作的命名 需要下载插件 Increment Selection 按照1的方法多选光标之后&am…...

【Linux】管道通信和 system V 通信
文章目录 一、进程通信原理(让不同进程看到同一份资源)二、管道通信2.1 管道原理及其特点2.1 匿名管道和命名管道 三、共享内存通信3.1 共享内存原理3.2 创建和关联共享内存3.3 去关联、ipc 指令和删除共享内存 四、消息队列和信号量(了解&am…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...
关于 WASM:1. WASM 基础原理
一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

AirSim/Cosys-AirSim 游戏开发(四)外部固定位置监控相机
这个博客介绍了如何通过 settings.json 文件添加一个无人机外的 固定位置监控相机,因为在使用过程中发现 Airsim 对外部监控相机的描述模糊,而 Cosys-Airsim 在官方文档中没有提供外部监控相机设置,最后在源码示例中找到了,所以感…...
虚拟电厂发展三大趋势:市场化、技术主导、车网互联
市场化:从政策驱动到多元盈利 政策全面赋能 2025年4月,国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》,首次明确虚拟电厂为“独立市场主体”,提出硬性目标:2027年全国调节能力≥2000万千瓦࿰…...

android13 app的触摸问题定位分析流程
一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...

Python 实现 Web 静态服务器(HTTP 协议)
目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1)下载安装包2)配置环境变量3)安装镜像4)node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1)使用 http-server2)详解 …...

抽象类和接口(全)
一、抽象类 1.概念:如果⼀个类中没有包含⾜够的信息来描绘⼀个具体的对象,这样的类就是抽象类。 像是没有实际⼯作的⽅法,我们可以把它设计成⼀个抽象⽅法,包含抽象⽅法的类我们称为抽象类。 2.语法 在Java中,⼀个类如果被 abs…...