当前位置: 首页 > news >正文

【源码】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题整数转换英文表示

题目&#xff1a; 题解&#xff1a; class Solution {String[] singles {"", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine"};String[] t…...

数据结构之深入理解简单选择排序:原理、实现与示例(C,C++)

文章目录 一、简单选择排序原理二、C/C代码实现总结&#xff1a; 在计算机科学中&#xff0c;排序算法是一种非常基础且重要的算法。简单选择排序&#xff08;Selection Sort&#xff09;作为其中的一种&#xff0c;因其实现简单、易于理解而受到许多初学者的喜爱。本文将详细介…...

使用vscode搜索打开的文件夹下的文件

右键空白处打开命令面板 摁一次删除键&#xff0c;删除掉图中的大于号 这样就能够找到例化的模块&#xff0c;文件具体在哪个位置&#xff0c;然后打开了...

力扣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入门

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

WPF+Mvvm项目入门完整教程-仓储管理系统(二)

目录 一、搭建一个主界面框架二、实现步骤1.主界面区域划分2.主界面区域实现 一、搭建一个主界面框架 主要实现主界面的框架样式和基础功能。这里特别说明一下&#xff0c;由于MvvmLight 已经过时不在维护&#xff0c;本项目决定将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子句 作用范围&#xff1a;WHERE子句主要用于过滤FROM子句返回的结果集。它可以在SELECT、UPDATE、DELETE语句中使用&#xff0c;以限制哪些行被包含在最终的查询结果中&#xff0c;或者哪些行被更新或删除。应用场景&#xff1a;当需要基于某些条件过滤结果集时&#xf…...

filebeat把日志文件上传到Es中配置(ES7版本)

默认的filebeat配置会把所有的索引都放到一个文件中&#xff0c;通过摸索发现可以自定义索引的名字、模板、生命周期 &#xff08;重点注意&#xff09;该配置文件只适应于ES版本是7&#xff0c;不适应于8的版本&#xff0c;两个版本的配置文件差异很大 /app/logs/info.log日…...

Vue Router基础

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

Apache压测工具ab(Apache Bench)工具的下载安装和使用示例

场景 Jmeter进行http接口压力测试&#xff1a; Jmeter进行http接口压力测试_接口压测两万量-CSDN博客 上面讲压测工具Jmeter的使用&#xff0c;下面介绍另外一个ab(Apache Bench)压测工具的使用。 apache bench apache bench是apache自带的压力测试工具。 ab不仅可以对ap…...

IPIDEA与Python爬虫:联手解锁全球电商数据宝库

IPIDEA与Python爬虫&#xff1a;联手解锁全球电商数据宝库 如何运用代理IP在电商领域进行高效数据采集。特别是在遭遇访问限制的情况下&#xff0c;如何优雅地绕过那些恼人的访问管理机制。当然&#xff0c;在我们的探险之旅中&#xff0c;开源神器PlugLink也将适时出场&#…...

Fine-BI学习笔记

官方学习文档&#xff1a;快速入门指南- FineBI帮助文档 FineBI帮助文档 (fanruan.com) 1.零基础入门 1.1 功能简介 完成四个流程&#xff1a;新建分析主题、添加数据、分析数据、分享协作。 示例数据获取&#xff1a;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 键&#xff0c; 选择需要批量操作的位置 如果是多行&#xff0c;则按住 altshift 键 可以直接操作 但是有时候比如变量命名&#xff0c;可能需要递增操作的命名 需要下载插件 Increment Selection 按照1的方法多选光标之后&am…...

【Linux】管道通信和 system V 通信

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

智慧医疗能源事业线深度画像分析(上)

引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...

FastAPI 教程:从入门到实践

FastAPI 是一个现代、快速&#xff08;高性能&#xff09;的 Web 框架&#xff0c;用于构建 API&#xff0c;支持 Python 3.6。它基于标准 Python 类型提示&#xff0c;易于学习且功能强大。以下是一个完整的 FastAPI 入门教程&#xff0c;涵盖从环境搭建到创建并运行一个简单的…...

连锁超市冷库节能解决方案:如何实现超市降本增效

在连锁超市冷库运营中&#xff0c;高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术&#xff0c;实现年省电费15%-60%&#xff0c;且不改动原有装备、安装快捷、…...

电脑插入多块移动硬盘后经常出现卡顿和蓝屏

当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时&#xff0c;可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案&#xff1a; 1. 检查电源供电问题 问题原因&#xff1a;多块移动硬盘同时运行可能导致USB接口供电不足&#x…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具

文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

【Go】3、Go语言进阶与依赖管理

前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课&#xff0c;做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程&#xff0c;它的核心机制是 Goroutine 协程、Channel 通道&#xff0c;并基于CSP&#xff08;Communicating Sequential Processes&#xff0…...

Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)

目录 一、&#x1f44b;&#x1f3fb;前言 二、&#x1f608;sinx波动的基本原理 三、&#x1f608;波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、&#x1f30a;波动优化…...

python执行测试用例,allure报乱码且未成功生成报告

allure执行测试用例时显示乱码&#xff1a;‘allure’ &#xfffd;&#xfffd;&#xfffd;&#xfffd;&#xfffd;ڲ&#xfffd;&#xfffd;&#xfffd;&#xfffd;ⲿ&#xfffd;&#xfffd;&#xfffd;Ҳ&#xfffd;&#xfffd;&#xfffd;ǿ&#xfffd;&am…...

深度学习习题2

1.如果增加神经网络的宽度&#xff0c;精确度会增加到一个特定阈值后&#xff0c;便开始降低。造成这一现象的可能原因是什么&#xff1f; A、即使增加卷积核的数量&#xff0c;只有少部分的核会被用作预测 B、当卷积核数量增加时&#xff0c;神经网络的预测能力会降低 C、当卷…...

Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?

在大数据处理领域&#xff0c;Hive 作为 Hadoop 生态中重要的数据仓库工具&#xff0c;其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式&#xff0c;很多开发者常常陷入选择困境。本文将从底…...