一、概述
jdbc(java database connectivity)是java和数据库之间的一个桥梁,是一个「规范」而不是一个实现,能够执行sql语句。jdbc由一组用java语言编写的类和接口组成。各种不同类型的数据库都有相应的实现,注意:本文中的代码都是针对mysql数据库实现的。
先看一个案例:
public class jdbcdemo {
public static final string url = "jdbc:mysql://localhost:3306/mblog";
public static final string user = "root";
public static final string password = "123456";
public static void main(string[] args) throws exception {
class.forname("com.mysql.jdbc.driver");
connection conn = drivermanager.getconnection(url, user, password);
statement stmt = conn.createstatement();
resultset rs = stmt.executequery("select id, name, age from m_user where id =1");
while (rs.next()) {
system.out.println("name: " + rs.getstring("name") + " :年龄" + rs.getint("age"));
}
}
}二、jdbc 连接路径
- 数据库驱动:class.forname(“com.mysql.jdbc.driver”);
- 获取链接:connection conn = drivermanager.getconnection(url, user, password);
- 创建statement或者preparedstatement对象: statement stmt = conn.createstatement();
- 执行sql数据库查询:resultset rs = stmt.executequery(“select id, name, age from m_user where id =1”);
- 解析结果集:system.out.println(“name: “+rs.getstring(“name”)+” :年龄”+rs.getint(“age”));
- 最后就是各种资源的关闭。
- 数据库驱动
- 安装好数据库之后,应用程序是不能直接使用数据库的,必须要通过相应的数据库驱动程序,通过驱动程序去和数据库打交道。其实也就是数据库厂商的jdbc接口实现,即对connection等接口的实现类的jar文件。
driver接口:此接口是提供给数据库厂商实现的。比如说mysql的,需要依赖对应的jar包
mysql数据库对应的实现驱动实现类:
package com.mysql.cj.jdbc;
import java.sql.sqlexception;
public class driver extends nonregisteringdriver implements java.sql.driver {
static {
try {
//注册驱动
java.sql.drivermanager.registerdriver(new driver());
} catch (sqlexception e) {
throw new runtimeexception("can't register driver!");
}
}
public driver() throws sqlexception {
}
}drivermanager是rt.jar包下的类,(rt=runtime),把程序需要驱动类注册进去。
//drivermanager类中的方法
public static synchronized void registerdriver(java.sql.driver driver,driveraction da)throws sqlexception{
/* register the driver if it has not already been added to our list */
if(driver!=null){
registereddrivers.addifabsent(new driverinfo(driver,da));
}else{
// this is for compatibility with the original drivermanager
throw new nullpointerexception();
}
println("registerdriver: "+driver);
}类似的,可以加载其它厂商的驱动
- oracle驱动:class.forname(“oracle.jdbc.driver.oracledriver”);
- sql server驱动:class.forname(“com.microsoft.jdbc.sqlserver.sqlserverdriver”);
获取链接
看起来只有这一行代码
connection conn = drivermanager.getconnection(url, user, password);
深入聊聊这行代码,到底底层是怎么连接数据库的?
方法三个参数:链接地址,用户名和密码。
public 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()));
}获取连接的关键代码adriver.driver.connect(url,info); 这个方法是每个数据库驱动自己的实现的。
// worker method called by the public getconnection() methods.
private static connection getconnection(string url,java.util.properties info,class caller)throws sqlexception{
classloader callercl = caller != null ? caller.getclassloader() : null;
sqlexception reason = null;
//遍历气门注册的数据库驱动
for(driverinfo adriver:registereddrivers){
try{
//获取连接
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;
}
}
}
}获取连接的关键代码adriver.driver.connect(url,info); 这个方法是每个数据库驱动自己的实现的。
package com.mysql.cj.jdbc;
public class nonregisteringdriver implements java.sql.driver {
@override
public java.sql.connection connect(string url, properties info) throws sqlexception {
//部分无关键要的代码省略
//...
//下面是重点
//connectionurl从这个类名应该能猜到还不到真正连接的,只是创建一个连接url相关信息封装。
connectionurl constr = connectionurl.getconnectionurlinstance(url, info);
switch (constr.gettype()) {
//single_connection("jdbc:mysql:", hostscardinality.single), //
case single_connection:
//这里就是获取一个实例,连接就在这里面产生的
return com.mysql.cj.jdbc.connectionimpl.getinstance(constr.getmainhost());
case loadbalance_connection:
return loadbalancedconnectionproxy.createproxyinstance((loadbalanceconnectionurl) constr);
case failover_connection:
return failoverconnectionproxy.createproxyinstance(constr);
case replication_connection:
return replicationconnectionproxy.createproxyinstance((replicationconnectionurl) constr);
default:
return null;
}
}
}
public static jdbcconnection getinstance(hostinfo hostinfo) throws sqlexception {
return new connectionimpl(hostinfo);
}connectionimpl构造方法里有调用createnewio方法:
@override
public void createnewio(boolean isforreconnect){
synchronized (getconnectionmutex()){
try{
if(!this.autoreconnect.getvalue()){
connectonetryonly(isforreconnect);
return;
}
connectwithretries(isforreconnect);
}catch(sqlexception ex){
}
}
}
private void connectonetryonly(boolean isforreconnect)throws sqlexception{
exception connectionnotestablishedbecause=null;
jdbcconnection c=getproxy();
//又看到熟悉的connet方法,
//其中,这里的session是nativesession
this.session.connect(this.orighostinfo,this.user,this.password,this.database,drivermanager.getlogintimeout()*1000,c);
this.session.setqueryinterceptors(this.queryinterceptors);
}
public void connect(hostinfo hi,string user,string password,string database,int logintimeout,transactioneventhandler transactionmanager)throws ioexception{
socketconnection socketconnection=new nativesocketconnection();
//看到socket连接了,后续就是socket的连接数据库的过程了
socketconnection.connect(this.hostinfo.gethost(),this.hostinfo.getport(),this.propertyset,getexceptioninterceptor(),this.log,logintimeout);
this.protocol.connect(user,password,database);this.protocol.getserversession().seterrormessageencoding(this.protocol.getauthenticationprovider().getencodingforhandshake());
}
com.mysql.cj.protocol.a.nativesocketconnection#connect
java
@override
public void connect(string hostname, int portnumber, propertyset propset, exceptioninterceptor excinterceptor, log log, int logintimeout) {
this·mysqlsocket = this.socketfactory.connect(this.host, this.port, propset, logintimeout);
//...
}
这里的socketfactory是standardsocketfactory。所以也就是调用的是standardsocketfactory的connect方法:
java
public t connect(string hostname, int portnumber, propertyset pset, int logintimeout) throws ioexception {
this.rawsocket = createsocket(pset);
this.rawsocket.connect(sockaddr, getrealtimeout(connecttimeout));
}
protected socket createsocket(propertyset props) {
return new socket();
}三、总结
数据库驱动依赖spi类加载机制
获取连接是通过socket与数据库取得连接的
到此这篇关于java深度解剖jdbc的底层实现原理的文章就介绍到这了,更多相关java jdbc原理内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论