当前位置: 首页 > 科技观察

想不到你是这样的JDBC

时间:2023-03-16 20:41:02 科技观察

本文将介绍MySQLClient和Server的通信原理,以及JavaJDBC的工作原理。什么是JDBC的Type4,什么是Type3?一、MySQLClient&Server我们在操作数据库的时候,总是通过GUI数据管理工具或者命令行连接到MySQLServer,然后进行一系列的数据库操作。创建、操作表和表中的数据等。这时候,这一系列的GUI管理工具,或者说命令行,就是一个MySQLClient,然后将Client的一系列操作命令发送给Server。这里在发送的时候,按照MySQL的规范,将Client的命令一条一条发送出去。更直观的理解,MySQL的Client和Server相当于Socket通信中的一个Client和Server,按照约定的协议格式相互通信。二、什么是JDBC?什么是JDBC?你会脱口而出,不就是通过它连接图书馆吗。这种理解只是其中的一小部分,“洒”了。JDBC全称:TheJavaDatabaseConnectivity应该从两个方面来理解。APIDriverAPI,首先是一个标准,不是针对特定的数据库,作为一种高层抽象,提供了Java语言和众多数据库之间的连接。通过JDBCAPI,我们不再需要根据不同的数据库使用不同的操作方式,而是使用一个标准的操作来实现“WriteOnce,Runanywhere”。API既然是标准,就需要有对应的实现。这里的Driver是各个数据库厂商按照标准实现的。这就是为什么在开发应用程序时,使用MySQL连接器连接MySQL,使用Oracle驱动程序连接Oracle。毕竟只有各个厂商才知道如何与自己厂商的数据库进行交互,所以根据标准,各个厂商都开发了自己的Connector。下图来自官方文档,描述了JDBC的作用以及在请求中的位置。图中左边也就是Type4,通过Driver直接连接数据库服务器。这也是最常用的,通过Driver,将JDBC请求转换成数据库服务器可以识别的协议格式。图中右侧称为Type3,将JDBC请求通过Driver转换成中间件的协议格式。以MySQL为例,我们发现JDBC的运行本质上相当于一个MySQLClient。通过Driver,将应用程序中的查询、删除等操作“翻译”成MySQLServer可以识别的协议格式。然后传递执行。因此,整个JDBC可以概括为以下三件事:创建数据库连接,发送SQL语句,处理请求结果JDBC概括了两部分,数据库服务提供者,开发XXXDriver,应用开发者使用Driver来实现连接到数据库并执行数据库操作。这样应用开发者就不需要关心底层与数据库交互时协议的实现,如何请求连接、交互等,可以更专注于自己的业务。否则,每个开发者都需要处理一次数据交互协议,繁琐困难,重复劳动。3、MySQLconnector-J的部分源码有了上面的“理论”知识,我们再来看一些东西。MySQL驱动包是开源的,我们可以很方便的下载和理解实现。JDBC最传统的使用方式一般是这样:Connectionc=DriverManager.getConnection(url,user,pwd);statementstmt=c.createStatmentstmt.executeQuery在获取结果getConnection时,一般需要提供一个URL。这个网址也是写的固定的。比如mysql就是jdbc:mysql://。这部分符合规范。同时在Driver代码中,通过解析URL获取需要连接的host、port等连接参数。publicPropertiesparseURL(Stringurl,Propertiesdefaults)throwsjava.sql.SQLException{PropertiesurlProps=(defaults!=null)?newProperties(defaults):newProperties();if(url==null){returnnull;}if(!StringUtils.startsWithIgnoreCase(url,URL_PREFIX)&&!StringUtils.startsWithIgnoreCase(url,MXJ_URL_PREFIX)&&!StringUtils.startsWithIgnoreCase(url,LOADBALANCE_URL_PREFIX)&&!StringUtils.startsWithIgnoreCase(url,REPLICATION_URL_PREFIX){returnnull;}intbeginningOfSlashes)=url.index(StringUtils.startsWithIgnoreCase(url,MXJ_URL_PREFIX)){urlProps.setProperty("socketFactory","com.mysql.management.driverlaunched.ServerLauncherSocketFactory");}查看这部分源码可以发现,除了我们常用的url配置,还可以用在其中,loadbalance配置等。我获得了知识。DriverManager.getConnection(xx,xx,xx)方法最终会调用ServiceProvider已经加载的Driver中可用的驱动,调用驱动的getConnection方法,对应Mysql的源码,也就是下面那个,以及焦点是com.mysql.jdbc。ConnectionImpl.getInstance`publicjava.sql.Connectionconnect(Stringurl,Propertiesinfo){if(url==null){throwSQLError.createSQLException(Messages.getString("NonRegisteringDriver.1"),SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE,null);}if(StringUtils.startsWithIgnoreCase(url,LOADBALANCE_URL_PREFIX)){returnconnectLoadBalanced(url,info);}elseif(StringUtils.startsWithIgnoreCase(url,REPLICATION_URL_PREFIX)){returnconnectReplicationConnection(url,info);}Propertiesprops=null;if((url,infroURL))==null){returnnull;}if(!"1".equals(props.getProperty(NUM_HOSTS_PROPERTY_KEY))){returnconnectFailover(url,info);}try{ConnectionnewConn=com.mysql.jdbc.ConnectionImpl.getInstance(host(props),端口(道具),道具,数据库(道具),网址);returnnewConn;}我们看看getInstance具体是干什么的?受保护的静态连接获取实例(StringhostToConnectTo,intportToConnectTo,Propertiesinfo,StringdatabaseToConnectTo,Stringurl)throwsSQLException{if(!Util.isJdbc4()){returnnewConnectionImpl(hostToConnectTo,portToConnectTo,信息,databaseToConnectTo,url);}返回(连接)Util.handleNewInstance(JDBC_4_CONNECTION[CTOR]{hostToConnectTo,Integer.valueOf(portToConnectTo),info,databaseToConnectTo,url},null);}this.io=newMysqlIO(newHost,newPort,mergedProps,getSocketFactoryClassName(),getProxy(),getSocketTimeout(),this.largeRowSizeThreshold.getValueAsInt());this.io.doHandshake(this.user,this.password,this.database);看一下,先通过MysqlIO建立IO连接,然后进行握手//savelastexceptiontopropagatetocallerifconnectionfailsS??ocketExceptionlastException=null;.Someversionsof//MySQL不监听IPv6地址所以尝试所有地址.for(inti=0;i(acom.mysql.jdbc.JDBC4Connection)atcom.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1861)atcom.mysql.jdbc.PreparedStatement.executeQuery(PreparedStatement.java:1962)背后的原理和我们前面说的一样,比较简单,就是通过一个TCPSocket的方法,得到之后OutputStream,安装的SQL,执行的时候,写入到这个Output,发送到Mysql服务器。返回值是怎么得到的?将返回的Buffer转换成ResultSetResultSetInternalMethodsrs=readAllResults(callingStatement,maxRows,resultSetType,resultSetConcurrency,streamResults,catalog,resultPacket,false,-1L,cachedMetadata);另外,在实际业务开发中,对于代码中获取的一个Connection,可能会遇到网络抖动、数据库服务异常等情况,在出现连接问题之前,我们可以先检查连接是否可用,避免继续使用有问题的连接,导致问题持续存在。检查连接是否可用,执行最简单的`select1`即可判断是否有异常。当然,在JDBC标准中,也有一个方法isValid来检测连接是否可用。对于MySQLConnctor-J客户端通过向服务器发送ping命令来检测连接状态。综上所述,我们通过几个部分介绍了MySQLClient和Server的交互原理,以及JDBC是什么以及它是如何与Server进行交互的。顺便分享一下最近和数据库连接相关的一个小插曲。处理了一个问题,添加了数据库连接检查,功能无误,上线了。上线后不久,另一家服务商报警,说我们发送了一条数据库命令,它无法处理。黑色问号脸。我只是通过获取数据库状态的getAttribute来检查连接。据说他们收到了showxxxstatus之类的命令。那为什么不能识别呢?仔细问了下,因为他们提供的特殊代理服务只实现了MySQL的部分命令解析,所以对应的showxxx是不支持的,而我们的项目默认所有客户端都支持全套命令,造成问题。之后又改了一个check方法,解决了告警问题。所以在开发的时候,还需要考虑接入的服务是否会按照规范来实现。【本文为专栏作家“侯书城”原创稿件,转载请通过作者微信公众号“Tomcat物语”获得授权】点此查看本作者更多好文