前言
本篇文章主要介绍JDBC的基本原理、驱动开发方法,了解原理后可以更进一步基于JDBC去访问数据库,实现应用程序开发。期望大家通过本文能够对JDBC驱动有一定的了解,并能够参与openGauss社区开发实践活动,积极探索openGauss社区、贡献openGauss社区。
一、JDBC驱动
1.1 基本介绍
JDBC( Java DataBase Connectivity ) 称为Java数据库连接,是一种访问数据库访问的标准应用程序API 。
为什么需要JDBC:不同的数据库通常会有不同的通信协议, 当一个程序使用不同的数据库时,就不得不按照厂商API写出几套连接程序;而厂商API可能在依赖操作系统功能,这将带来严重的跨平台依赖和重复代码问题。
JDBC的出现解决了这个问题,它提供了一致的编码规范,无论是哪一种数据库,在应用程序层面,都是通过一致的API去访问。
当然由于各个数据库的实现差异较大,不同的数据库驱动程序各不相同,JDBC通常由数据库厂商提供:
1.2 标准接口
JDBC定义了一组标准的接口用于访问数据库,它们分别是数据库连接对象(Connection), 语句(Statement)以及结果集(ResultSet)。注意JDBC所有对象都必须显式的关闭,否则将造成资源泄露。它们之间的关系如下图示:连接创建语句,语句执行返回结果集。
1.3 openGauss驱动简介
openGauss驱动是由openGauss开源社区提供的,使用Java语言编写的专用于访问openGauss数据库的驱动程序。
下载地址:https://opengauss.org/zh/download/
jdbc提供一键式编译脚本“sh build.sh”即可完成编译打包。
openGauss-jdbc已发布在maven中心仓,访问坐标如下:
注意版本号的差别:自3.1.0版本开始,均会提供带"-og"后缀的版本,它们的功能完全一致,差别点是否与pg驱动共存,如:
"3.1.0"的版本中,jar包内的包名为org.postgresql,它可以完全替换pg驱动类,但是它不可与pg驱动共存。
"3.1.0-og"版本中,jar包内的包名为org.opengauss,它可以与pg数据库驱动包共存。
1.4 小结
以上内容是关于JDBC的介绍,让大家了解了JDBC的作用,同时也可以掌握openGauss社区的jdbc开源地址、发布包等内容,为下一步的驱动开发做好准备。
二、openGauss驱动原理
2.1 基本介绍
openGauss-jdbc在是TCP/IP 协议之上实现了一套基于消息的通信协议。主要包括以下内容:
-
建立和取消连接的消息流
-
通信协议中使用的消息格式和类型
-
处理请求的消息流
-
Copy 子协议
-
批量插入子协议
openGauss 是多线程架构,守护线程 Postmaster 为每个连接分配一个后台线程(backend),后台线程的分配是在协议处理之前进行的,每个后台进程自行负责协议的处理。在 openGauss 源码或者文档中,通常认为 ‘backend’ 和 ‘server’ 是等价的,表示服务端;同样,‘frontend’ 和 ‘client’ 是等价的,表示客户端(应用程序)。
2.2 认证协议介绍
openGauss-jdbc通信协议包括两个阶段:startup和normal。startup阶段主要完成客户端与数据库认证连接,正常则连接成功进入normal阶段。normal阶段客户端完成SQL命令的请求和数据库的响应。
startup是非通用消息,会将协议版本号和客户端连接的用户名、数据库和GUC参数发送给数据库,而normal阶段则使用通用的消息格式:
startup阶段认证过程如下:
-
auth request消息(13字节):R 4字节(长度) 4字节(认证协议) 4字节(随机盐值)
-
PasswordMesage消息: p 4字节(长度) 36字节(35字节md5密码+\0)
-
ParameterStatus消息(包括server_version,client_edcoding和DataStyle): S 4字节(长度) key\0value\0
-
BackendKeyData消息(13字节): K 4字节(长度) 4字节(pid码) 4字节(ckey)
-
ReadyForQuery消息(6字节): Z 4字节(长度) 1字节(状态码I->idle, T->事务中, E->出现错误)
2.3 查询过程协议
normal阶段有两种“子协议”来发送请求,分别是simple query和extended query。使用simple query时,客户端直接发送请求,数据库立即处理并返回结果;而extended query会将请求过程分为若干个步骤,以加快数据库的处理速度。
2.4 copy协议
为了高效导入/导出数据,openGauss支持copy命令,copy命令会使数据库的连接处于不同的数据处理模式下。
copy 子协议对应三种模式:
-
copy-in 导入数据,对应命令 COPY FROM STDIN
-
copy-out 导出数据,对应命令 COPY TO STDOUT
-
copy-both 用于 walsender,在主备间批量传输数据
以copy-in处理流程举例
2.5 代码结构
源码地址: https://gitee.com/opengauss/openGauss-connector-jdbc.git
核心入口来源于Driver类,由它来完成所有JDBC接口类的调用;
openGauss驱动也提供了负载均衡、逻辑复制、copy等特有实现内容。
2.6 属性配置
连接数据库时使用Driver.connect函数实现,属性配置既可以通过url传递也可以通过properties传递。
注意:properties仅接收字符串的value参数,同时传递时properties的参数优先级更高。
参考官方文档:https://docs-opengauss.osinfra.cn/zh/docs/latest/docs/DeveloperGuide/JDBC%E5%B8%B8%E7%94%A8%E5%8F%82%E6%95%B0%E5%8F%82%E8%80%83.html
更多配置参数可以从PGProperty.java中获得。
常见参数:PGDBNAME、PGHOST、PGPORT、user、password、loggerLevel、prepareThreshold、preparedStatementCacheQueries等。
常见的url串编写如下:
jdbc:opengauss://$PGHOST:$ PGPORT/$ PGDBNAME?key=value&key1=value1...
实例:
jdbc:opengauss://127.0.0.1:5432/postgres?allowEncodingChanges=true&characterEncoding=UTF-8
2.7 PBE核心流程
2.8 编译
openGauss的编译提供一键式编译脚本build.sh,代码仓中有详细的编译说明:
2.9 调试
openGauss代码仓中已经提供了全套的juin4测试用例,最快捷的调试方法就是在工程下面添加新的测试用例验证。
第一步:更新测试数据库的配置信息(build.properties),也可以使用build.local.properties文件。
第二步:添加测试case,进行驱动验证。
参考org.postgresql.v511. SelectFunctionTest进行纯jdbc测试case验证。或者参考org.postgresql.jdbc.DeepBatchedInsertStatementTest继承自BaseTest4进行标准case验证。
2.10 问题实战
以问题单:https://gitee.com/opengauss/openGauss-connector-jdbc/issues/I84URC为例讲解实战分析过程:
一、添加测试case验证问题是否复现:
主动添加org.postgresql.v511.SelectFunctionTest类,增加testFunctionQuery测试方法,同时增加loggerLevel=TRACE
二、通过日志可以看出来是因为SQL被截断所致,而SQL截断是否Parser. parseJdbcSql处理,可以继续跟进调试。
三、最终原因分析结果为错误的将含有function的字符串识别成了函数体创建,引起分割问题
参考解决PR:https://gitee.com/opengauss/openGauss-connector-jdbc/pulls/172
2.11 小结
在本部分中,大家应该了解openGauss jdbc驱动的协议、实现原理,掌握jdbc代码的编译、调试能力,可以参与到jdbc的社区问题实战活动。
三、纯JDBC应用程序开发
3.1 驱动加载原理
在jdk1.6以前,通常编写jdbc程序前需主动调用Class.formName(“org.opengauss.Driver”)实现驱动的加载。
在jdk1.6及以后,java支持SPI机制实现驱动自发现,不需要再额外的操作。
常见的驱动未发现异常(“No suitable driver found”),请仔细排查驱动包是否被加载。
3.2 标准应用开发
注意
JDBC的Connection/Statement/ResultSet都是不受JVM管理的资源,必须显式的申请和释放,否则会造成资源泄露!
3.3 逻辑复制
openGauss提供逻辑复制功能以实现同构/异构数据库间的数据同步。
代码参考: https://docs-opengauss.osinfra.cn/zh/docs/2.0.1/docs/Developerguide/%E7%A4%BA%E4%BE%8B-%E9%80%BB%E8%BE%91%E5%A4%8D%E5%88%B6%E4%BB%A3%E7%A0%81%E7%A4%BA%E4%BE%8B.html
3.4 小结
在本部分中,大家应该了解了openGauss jdbc的加载过程和基本的jdbc应用程序编写。
四、使用ORM框架和连接池的应用程序开发
4.1 ORM框架
ORM(Object Relational Mapping)框架采用元数据来描述对象与关系映射的细节,元数据一般采用XML格式,并且存放在专门的对象一映射文件中。简单理解为一种框架的格式,可以大大减少数据库的保存、删除、读取等重复性代码。
本次以MyBatis为例:https://mybatis.org/mybatis-3/zh_CN/index.html
完整示例代码参考: https://gitee.com/justbk/jdbc_test/blob/master/src/test/java/BlobTest.java
4.2 连接池
数据库连接池(Connection Pooling)是将数据库的多个连接统一管理起来的程序,它可以动态地进行连接的申请、使用、释放管理。由于数据库创建连接是非常大的开销,所以连接复用是其核心思想。本文以druid为例进行讲解。
源代码地址: https://gitee.com/justbk/jdbc_test/blob/master/src/test/java/DruidTest.java
由于连接池本身也是围绕JDBC规范而设计的,所以对ORM/连接池来说,使用用法基本一致,与数据库关系较小。
4.3 小结
在本部分中,大家应该了解了与驱动密切相关的ORM和连接池概念,以及掌握mybatis和druid的使用。