JDBC 底层原理

打印 上一主题 下一主题

主题 903|帖子 903|积分 2709

概述

JDBC(Java DataBase Connectivity)是Java和数据库之间的一个桥梁,是一个「规范」而不是一个实现,能够执行SQL语句。JDBC由一组用Java语言编写的类和接口组成。各种差别类型的数据库都有相应的实现,注意:本文中的代码都是针对MySQL数据库实现的。
先看一个案例:
  1. public class JdbcDemo {
  2.     public static final String URL = "jdbc:mysql://localhost:3306/mblog";
  3.     public static final String USER = "root";
  4.     public static final String PASSWORD = "123456";
  5.     public static void main(String[] args) throws Exception {
  6.         Class.forName("com.mysql.jdbc.Driver");
  7.         Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
  8.         Statement stmt = conn.createStatement();
  9.         ResultSet rs = stmt.executeQuery("SELECT id, name, age FROM m_user where id =1");
  10.         while (rs.next()) {
  11.             System.out.println("name: " + rs.getString("name") + " :年龄" + rs.getInt("age"));
  12.         }
  13.     }
  14. }
复制代码
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数据库对应的实现驱动实现类:
  1. package com.mysql.cj.jdbc;
  2. import java.sql.SQLException;
  3. public class Driver extends NonRegisteringDriver implements java.sql.Driver {
  4.     static {
  5.         try {
  6.             //注册驱动
  7.             java.sql.DriverManager.registerDriver(new Driver());
  8.         } catch (SQLException E) {
  9.             throw new RuntimeException("Can't register driver!");
  10.         }
  11.     }
  12.     public Driver() throws SQLException {
  13.     }
  14. }
复制代码
DriverManager是rt.jar包下的类,(rt=runtime),把程序需要驱动类注册进去。
  1. //DriverManager类中的方法
  2. public static synchronized void registerDriver(java.sql.Driver driver,DriverAction da)throws SQLException{
  3.         /* Register the driver if it has not already been added to our list */
  4.         if(driver!=null){
  5.             registeredDrivers.addIfAbsent(new DriverInfo(driver,da));
  6.         }else{
  7.         // This is for compatibility with the original DriverManager
  8.             throw new NullPointerException();
  9.         }
  10.         println("registerDriver: "+driver);
  11. }
复制代码
类似的,可以加载其它厂商的驱动

  • Oracle驱动:Class.forName("oracle.jdbc.driver.OracleDriver");
  • Sql Server驱动:Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver");
获取链接

看起来只有这一行代码
Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
深入聊聊这行代码,到底底层是怎么连接数据库的?
方法三个参数:链接地址,用户名和密码。
  1. public static Connection getConnection(String url,String user, String password) throws SQLException {
  2.         java.util.Properties info = new java.util.Properties();
  3.         if (user != null) {
  4.             info.put("user", user);
  5.         }
  6.         if (password != null) {
  7.             info.put("password", password);
  8.         }
  9.         return (getConnection(url, info, Reflection.getCallerClass()));
  10. }
复制代码
获取连接的关键代码aDriver.driver.connect(url,info); 这个方法是每个数据库驱动自己的实现的。
  1. // Worker method called by the public getConnection() methods.
  2. private static Connection getConnection(String url,java.util.Properties info,Class caller)throws SQLException{
  3.         ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
  4.         SQLException reason = null;
  5.         //遍历气门注册的数据库驱动
  6.         for(DriverInfo aDriver:registeredDrivers){
  7.             try{
  8.                 //获取连接
  9.                 Connection con = aDriver.driver.connect(url,info);
  10.                 if(con!=null){
  11.                     // Success!
  12.                     println("getConnection returning "+aDriver.driver.getClass().getName());
  13.                     return(con);
  14.                 }
  15.             }catch(SQLException ex){
  16.                 if(reason==null){
  17.                     reason=ex;
  18.                 }
  19.             }
  20.         }
  21. }
复制代码
获取连接的关键代码aDriver.driver.connect(url,info); 这个方法是每个数据库驱动自己的实现的。
  1. package com.mysql.cj.jdbc;
  2. public class NonRegisteringDriver implements java.sql.Driver {
  3.     @Override
  4.     public java.sql.Connection connect(String url, Properties info) throws SQLException {
  5.         //部分无关键要的代码省略
  6.         //...
  7.         
  8.         //下面是重点
  9.         //ConnectionUrl从这个类名应该能猜到还不到真正连接的,只是创建一个连接Url相关信息封装。
  10.         ConnectionUrl conStr = ConnectionUrl.getConnectionUrlInstance(url, info);
  11.         switch (conStr.getType()) {
  12.             //SINGLE_CONNECTION("jdbc:mysql:", HostsCardinality.SINGLE), //
  13.             case SINGLE_CONNECTION:
  14.                 //这里就是获取一个实例,连接就在这里面产生的
  15.                 return com.mysql.cj.jdbc.ConnectionImpl.getInstance(conStr.getMainHost());
  16.             case LOADBALANCE_CONNECTION:
  17.                 return LoadBalancedConnectionProxy.createProxyInstance((LoadbalanceConnectionUrl) conStr);
  18.             case FAILOVER_CONNECTION:
  19.                 return FailoverConnectionProxy.createProxyInstance(conStr);
  20.             case REPLICATION_CONNECTION:
  21.                 return ReplicationConnectionProxy.createProxyInstance((ReplicationConnectionUrl) conStr);
  22.             default:
  23.                 return null;
  24.         }
  25.     }
  26. }
  27. public static JdbcConnection getInstance(HostInfo hostInfo) throws SQLException {
  28.         return new ConnectionImpl(hostInfo);
  29. }
复制代码
ConnectionImpl构造方法里有调用createNewIO方法:
  1. @Override
  2. public void createNewIO(boolean isForReconnect){
  3.     synchronized (getConnectionMutex()){
  4.         try{
  5.             if(!this.autoReconnect.getValue()){
  6.                 connectOneTryOnly(isForReconnect);
  7.                 return;
  8.             }
  9.             connectWithRetries(isForReconnect);
  10.         }catch(SQLException ex){
  11.         }
  12.     }
  13. }
  14. private void connectOneTryOnly(boolean isForReconnect)throws SQLException{
  15.         Exception connectionNotEstablishedBecause=null;
  16.         JdbcConnection c=getProxy();
  17.         //又看到熟悉的connet方法,
  18.         //其中,这里的session是NativeSession
  19.         this.session.connect(this.origHostInfo,this.user,this.password,this.database,DriverManager.getLoginTimeout()*1000,c);
  20.         this.session.setQueryInterceptors(this.queryInterceptors);
  21. }
  22. public void connect(HostInfo hi,String user,String password,String database,int loginTimeout,TransactionEventHandler transactionManager)throws IOException{
  23.         SocketConnection socketConnection=new NativeSocketConnection();
  24.         //看到socket连接了,后续就是socket的连接数据库的过程了
  25.         socketConnection.connect(this.hostInfo.getHost(),this.hostInfo.getPort(),this.propertySet,getExceptionInterceptor(),this.log,loginTimeout);
  26.         this.protocol.connect(user,password,database);this.protocol.getServerSession().setErrorMessageEncoding(this.protocol.getAuthenticationProvider().getEncodingForHandshake());
  27. }
复制代码
com.mysql.cj.protocol.a.NativeSocketConnection#connect
  1. @Override
  2. public void connect(String hostName, int portNumber, PropertySet propSet, ExceptionInterceptor excInterceptor, Log log, int loginTimeout) {
  3.     this·mysqlSocket = this.socketFactory.connect(this.host, this.port, propSet, loginTimeout);
  4.     //...
  5. }
复制代码
这里的socketFactory是StandardSocketFactory。所以也就是调用的是StandardSocketFactory的connect方法:
  1. public T connect(String hostname, int portNumber, PropertySet pset, int loginTimeout) throws IOException {
  2.     this.rawSocket = createSocket(pset);
  3.     this.rawSocket.connect(sockAddr, getRealTimeout(connectTimeout));
  4. }
  5. protected Socket createSocket(PropertySet props) {
  6.     return new Socket();
  7. }
复制代码
总结

数据库驱动依赖SPI类加载机制
获取连接是通过socket与数据库取得连接的

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

数据人与超自然意识

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表