Linkis JDBC模块设计介绍

Stella981
• 阅读 699

目录

一、 背景介绍

二、 使用介绍

(1)引入依赖模块

(2)建立测试类

三、 JDBC模块设计方案

(1)驱动类UJESSQLDriver

(2)JDBC连接器UJESSQLConnection

( 3)执行对象UJESSQLStatement/UJESSQLPreStatement

(4)结果集UJESSQLResultSet

(5)错误码方案

四、 实现方案总结

五、 参考文献

相关文章分享: Linkis JDBC是如何适配Tableau的?

Linkis JDBC模块设计介绍

 

01

背景介绍

Linkis作为大数据平台中间件,连接了底层的计算存储和上层的开发应用,统一了任务的调度和执行,在JDBC模块开发出来之前,向Linkis提交SQL执行任务到Spark、Hive执行支持websocket和restful的方式。为了多样化与Linkis的交互方式,便捷用户开发流程,轻量化客户端的任务提交过程,JDBC的支持无疑是非常值得考虑的。

JDBC(Java Data Base Connectivity, java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序。JDBC提供的主要功能是:1、同一个数据库建立连接;2、向数据库发送SQL语句;3、处理数据库返回的结果。

在Linkis中,JDBC模块属于Linkis-UJES下面的一个子模块,UJES即Unified Job Execution Service(统一作业执行服务),UJES是Linkis最初版本的雏形,提供了基础的任务提交和结果集查询的对外服务,查询时JDBC向Linkis-UJES客户端提交SQL执行,返回得到结果集,用户在只需要像使用mysql的JDBC那样操作,大大降低了学习成本,而实现这些功能仅仅需要用户引入一个Linkis JDBC的JAR包即可。

需要区分的是,Linkis还提供JDBC引擎,例如在DataSphereStudio中可以创建JDBC脚本通过JDBC引擎提交任务到Linkis,但该引擎仅模拟实现了JDBC的部分方法,并没有规范性的实现sun提供的JDBC 4.0的完整接口方案,无法向外提供规范的SDK服务。而本文所指的Linkis JDBC模块是实现了一套接口的完整方案。

Linkis JDBC模块设计介绍

02

使用介绍

(1)引入依赖模块

第一种方式在pom里面依赖JDBC模块:

 `<dependency>`
 
 
 ` <groupId>com.webank.wedatasphere.linkis</groupId>`
 
 
 ` <artifactId>linkis-ujes-jdbc</artifactId>`
 
 
 ` <version>0.9.1</version>`
 
 
 ` </dependency>`
 
 
 














 

注意如果引入不到该jar包,需要在ujes/jdbc目录里面执行mvn install -Dmaven.test.skip=true进行本地安装

第二种方式通过打包和编译

Step1:在Linkis项目中进入到ujes/jdbc目录然后在终端输入指令进行打包mvn assembly:assembly -Dmaven.test.skip=true 该打包指令会跳过单元测试的运行和测试代码的编译,并将JDBC模块需要的依赖一并打包进Jar包之中。

Step2:打包完成后在JDBC的target目录下会生成两个Jar包,Jar包名称中包含dependencies字样的那个就是我们需要的Jar包

(2)建立测试类

建立Java的测试类UJESClientImplTestJ,具体接口含义可以见注释:

public static void main(String[] args) throws SQLException, ClassNotFoundException {


   
   
   
 
    
    
    

   
   
   

Linkis JDBC模块设计介绍

    图
 
    
    
    2-1 Linkis JDBC
 
    
    
    任务执行结果

   
   
   

   
   
   
 
    
    
    

   
   
   

   
   
   
 
    
    
    

   
   
   

   
   
   
 
    
    
    

   
   
   

03

模块设计方案

Linkis JDBC模块设计的初衷是为了方便用户通过JDBC的方式,便捷的提交SQL任务到Linkis中执行,是客户端轻量化追求的一种体现,该模块的类大多以UJESSQL开头,表示JDBC模块属于linkis的ujes(Unified Job Execution Service,统一任务执行服务)模块的一部分。

Linkis的JDBC模块包含了五个关键的实现类:

  • UJESSQLDriver

  • UJESSQLConnection

  • UJESSQLStatement

  • UJESSQLPreStatement

  • UJESSQLResultSet

以及许多额外的辅助类,例如数据库元数据UJESSQLDatabaseMetaData,任务执行返回的结果集元数据UJESSQLResultMetaData等。

当UJESSQLDriver通过反射机制注册到DriverManager后,通过DM可以拿到UJESSQLConnection,接着便可以正常进行SQL任务提交和获取结果集,下面是结果集获取时的方法调用时序图:

Linkis JDBC模块设计介绍

下面将逐一介绍JDBC关键类在Linkis中的实现方案。

(1)驱动类UJESSQLDriver

在 JDBC的层次上,sun主要定义了一个接口Driver和两个类:DirverManager和DriverInfo,每个JDBC驱动程序必须实现 Driver接口,例如MySql的Connector/J驱动中,叫做com.mysql.jdbc.Driver,在Linkis的JDBC中的驱动实现类为UJESSQLDriver。使用时通过Class.forName("com.webank.wedatasphere.linkis.ujes.jdbc.UJESSQLDriver")的方法显示地让JVM尝试加载类,并相应的调用静态代码块完成驱动类的注册。UJESSQLDriver的主要代码如下:

static {

通过调用DriverManager的注册方法registerDriver将该驱动类注册到DriverManager中,当用户调用DriverManager的getConnection时,DriverManager会检索所有已经注册的驱动类,并根据驱动类的类名和请求URL中的类名进行对比,寻找出对应的驱动类。

(2)JDBC连接器UJESSQLConnection

Linkis JDBC中连接器为UJESSQLConnection,实现了java.sql.connection接口。注册驱动之后,可以通过传入指定的数据库连接路径,用户名和密码便可获取数据库连接对象。

conn = (UJESSQLConnection) DriverManager

DriverManager 的getConnection方法将传入的参数进行处理和转换,调用Driver的connect方法,再将参数传入UJESSQLConnection的构造器中初始化,返回给用户。下面是UJESSQLDriver实现的的connect方法:

override def connect(url: String, info: Properties): Connection = if(acceptsURL(url)) {

在构造好连接参数之后,会调用UJESClientFactory.getUJESClient(prop)方法创建一个新的ujesclient的linkis访问客户端,用于提交和查询linkis任务。

def getUJESClient(props: Properties):UJESClient = {

将创建好的ujesclient和数据库参数props传入UJESSQLConnection的构造器,最终得到一个完整的UJESSQLConnection对象。

(3)执行对象UJESSQLStatement/UJESSQLPreStatement

执行对象在整个JDBC连接和使用的生命周期中,属于请求保存者和执行者的身份,每个JDBC连接器在生成之后,可以通过调用连接器Connection的createStatement方法获取执行对象,类似地,也可以通过Connection的prepareStatement方法获取预执行对象。      

//获取执行对象

UJESSQLStatement中最为重要的方法execute作为提交SQL任务执行的入口,任务提交的执行主流程如下:

Step1 :调用hook修改sql。

Step2 :生成用于提交linkis任务的action。

Step3 :利用ujes客户端提交job到linkis执行。

Step4 :检测job的状态翻转。

Step5 :获取结果集ResultSet。

UJESSQLStatement中提交任务执行的Execute方法的代码:

override defexecute(sql: String): Boolean = throwWhenClosed {

同样的UJESPrepareStatement中的excute方法继承自Statement,原理一致。

UJESPrepareStatement与UJESStatement的不同之处在于,statement每次执行sql语句,相关数据库都要执行sql语句的编译,preparedstatement是预编译的, 且支持批处理。

(4)结果集UJESSQLResultSet

当用户提交完SQL任务到Linkis后,会检测用户的job是否已经完成,完成时调用getResultSet的方法获取结果集UJESSQLResultSet。

在UJESSQLResultSet初次加载的时候,java虚拟机会调用初始化的init()方法,该方法会执行三个初始化的步骤,用于构建结果集:

Step1:通过resultSetResultInit方法设置获取结果集相关的参数,如当前用户和结果集路径,然后通过ujesClient拿到结果集。

Step2:通过metaDataInit方法获取结果集的元数据。

Step3:通过resultSetInit方法获取结果集的内容。

private def init(): Unit = {

UJESSQLResultSet中的next()方法将currentRowCursor作为移动游标。每次从结果集中读取数据后都会相应地更新游标的位置。如果next方法返回true,则可以调用getXXX()方法获取相关字段数据,反之则说明当前游标并未指向一条有效记录,读取过程直接结束。

override def next(): Boolean = {

当next() 方法返回true时,会相应地调用getXXX()方法读取数据。以getString()方法为例:

override def getString(columnIndex: Int): String = {

该方法调用getColumnValue读取数据,并将其转化为String类型的值返回。

(5)错误码方案

为了便于用户利用Linkis JDBC提交SQL执行,减少错误排查时间,我们在Linkis JDBC中对常见的错误生成了错误码,尽量覆盖整个JDBC提交和执行过程。错误码编号范围初步设定在80000~80100,常见的错误类型包括:参数类型错误、方法暂不支持、操作逻辑出错以及返回类型错误等。

public enum UJESSQLErrorCode {

Linkis JDBC模块设计介绍

04

实现方案总结

Linkis JDBC模块设计的初衷是为了用户能够方便的通过JDBC的方式提交SQL任务到LInkis执行,在实现的过程中,我们参考了文章[1]进行初步的框架设计,实现过程中对于任务的提交和封装参考了Linkis ujes中与job相关的文档,阅读了一些JDBC相关的文章[2][3]。设计的过程中仍有一部分非必要的接口没有实现,这是参考Kylin、Hive等项目中JDBC模块设计综合考量后的结果,在不影响使用效果的前提下降低开发成本。

Linkis本身作为大数据产品的连接器,具有强大的集成和可拓展性,JDBC模块也是Linkis的向外兼容的一个具体实现,期待服务于社区一岁多的Linkis能够茁壮成长,在大家的共同栽培下枝繁叶茂。

Linkis JDBC模块设计介绍

05

参考文献

[1] create-your-own-type-3-jdbc-driver

https://www.javaworld.com/article/2074249/create-your-own-type-3-jdbc-driver--part-1.html

[2] Java JDBC的优雅设计

https://blog.csdn.net/yisizhu/article/details/104025220

[3] Class.forName加载JDBC驱动程序时,底层都做了些什么???

https://www.cnblogs.com/liuxianan/archive/2012/08/04/2623258.html

WeDataSphere,BIG DATA MADE EASY.

用心做一个有温度的开源社区

欢迎关注

Linkis JDBC模块设计介绍

扫码关注我们

微信号公众号 : WeDataSphere

GitHub:WeDataSphere

如果喜欢我们的产品或文章,请给我们的GitHub点上你宝贵的star和fork哦~~

本文分享自微信公众号 - WeDataSphere(gh_273e85fce73b)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
2年前
java将前端的json数组字符串转换为列表
记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表。前端数据转化与请求varcontracts{id:'1',name:'yanggb合同1'},{id:'2',name:'yanggb合同2'},{id:'3',name:'yang
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Stella981 Stella981
2年前
Python之time模块的时间戳、时间字符串格式化与转换
Python处理时间和时间戳的内置模块就有time,和datetime两个,本文先说time模块。关于时间戳的几个概念时间戳,根据1970年1月1日00:00:00开始按秒计算的偏移量。时间元组(struct_time),包含9个元素。 time.struct_time(tm_y
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这