Mybatis数据库驱动

最近在学习mybatis的源码,有一个databaseIdProvider根据不同数据库执行不同sql的功能,我正好有一个mysql还有一个瀚高数据库,就去试了一下,使用如下

pom文件导入两个数据库的驱动

xml


    mysql
    mysql-connector-java
    8.0.13



    com.highgo
    HgdbJdbc
    6.2.2

XML 复制 全屏

主启动类.java

java

public class MybatisHelloWorld {
    public static void main(String[] args) throws Exception {
        String resource = "org/mybatis/config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession session = sqlSessionFactory.openSession();
        UserMapper mapper = session.getMapper(UserMapper.class);
        List users = mapper.getUsers(1);
        session.close();
    }
}

User.java

java

public class User {
    private Integer id;
    private String name;
    private Integer age;
    //....getter setter 构造..
}

UserMapper.java

java

public interface UserMapper {
    List getUsers(int age);
}

UserMapper.xml

xml




    

    


Mybatis配置文件

xml


    
        
            
            
                
                
                
                
            
        

        
            
            
                
                
                
                
            
        
    


    
        
        
    


    
        
    


当我把mybatis配置文件中的环境设置为,代码执行结果如下

Mybatis数据库驱动_第1张图片

然后修改环境设置为后,代码执行结果如下

Mybatis数据库驱动_第2张图片

不知道您有没有看出问题所在,在上面的mybatis配置文件中highgo环境的驱动是com.mysql.cj.jdbc.Driver 但是能连接上瀚高的数据库并且能正常执行sql

当时我也发现这个问题了,于是想研究下原因

首先要找到是哪一段代码进行的操作,那么这里肯定是创建连接的时候,因为驱动不对的话是连接不上的,于是跟着这个思路就去寻找

最后找到方法栈如下

  • doGetConnection:200, UnpooledDataSource (org.apache.ibatis.datasource.unpooled)
  • doGetConnection:196, UnpooledDataSource (org.apache.ibatis.datasource.unpooled)
  • getConnection:93, UnpooledDataSource (org.apache.ibatis.datasource.unpooled)
  • popConnection:407, PooledDataSource (org.apache.ibatis.datasource.pooled)
  • getConnection:89, PooledDataSource (org.apache.ibatis.datasource.pooled)
  • getDatabaseProductName:82, VendorDatabaseIdProvider (org.apache.ibatis.mapping)
  • getDatabaseName:66, VendorDatabaseIdProvider (org.apache.ibatis.mapping)
  • getDatabaseId:53, VendorDatabaseIdProvider (org.apache.ibatis.mapping)
  • databaseIdProviderElement:305, XMLConfigBuilder (org.apache.ibatis.builder.xml)
  • parseConfiguration:123, XMLConfigBuilder (org.apache.ibatis.builder.xml)
  • parse:97, XMLConfigBuilder (org.apache.ibatis.builder.xml)
  • build:82, SqlSessionFactoryBuilder (org.apache.ibatis.session)
  • build:67, SqlSessionFactoryBuilder (org.apache.ibatis.session)
  • main:32, MybatisHelloWorld (org.mybatis)

UnpooledDataSource.java

java

private Connection doGetConnection(Properties properties) throws SQLException {
    initializeDriver();
    Connection connection = DriverManager.getConnection(url, properties);
    configureConnection(connection);
    return connection;
}

java

private synchronized void initializeDriver() throws SQLException {
    //判断这个驱动是否注册过
    if (!registeredDrivers.containsKey(driver)) {
        Class driverType;
        try {
            if (driverClassLoader != null) {
                driverType = Class.forName(driver, true, driverClassLoader);
            } else {
                driverType = Resources.classForName(driver);
            }
            Driver driverInstance = (Driver)driverType.newInstance();
            DriverManager.registerDriver(new DriverProxy(driverInstance));
            registeredDrivers.put(driver, driverInstance);
        } catch (Exception e) {
            throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e);
        }
    }
}

先判断需要加载的驱动是否已经注册了

Mybatis数据库驱动_第3张图片

那这里面的两个驱动是从哪里来的呢?

就在这个UnpooledDataSource类中的静态块里面

java

static {
    Enumeration drivers = DriverManager.getDrivers();
    while (drivers.hasMoreElements()) {
        Driver driver = drivers.nextElement();
        registeredDrivers.put(driver.getClass().getName(), driver);
    }
}

而DriverManager中有一个集合用来存储所有已经注册的数据库连接驱动

java

public class DriverManager {

    // List of registered JDBC drivers
    private final static CopyOnWriteArrayList registeredDrivers = new CopyOnWriteArrayList<>();
	//....
    public static java.util.Enumeration getDrivers() {
        java.util.Vector result = new java.util.Vector<>();

        Class callerClass = Reflection.getCallerClass();

        // Walk through the loaded registeredDrivers.
        for(DriverInfo aDriver : registeredDrivers) {
            // If the caller does not have permission to load the driver then
            // skip it.
            if(isDriverAllowed(aDriver.driver, callerClass)) {
                result.addElement(aDriver.driver);
            } else {
                println("    skipping: " + aDriver.getClass().getName());
            }
        }
        return (result.elements());
    }
    //......
}

那么问题又来了,DriverManager里面的瀚高数据库驱动啥时候放进去的呢

在学java基础的jdbc时,肯定都写过类似这样的代码

java

public static void main(String[] args) throws Exception {
    Class.forName("com.mysql.cj.jdbc.Driver");
    Connection con= DriverManager.getConnection("jdbc:mysql://localhost:3306/xxx","root","XXXXXX");
    Statement stat=con.createStatement();
	//......
}

当时这段Class.forName("com.mysql.cj.jdbc.Driver");就告诉你是加载驱动,有的博客写了这段代码,有的没写,具体操作一直都不清楚

首先JDK5版本以后可以不用显式调用这段话,DriverManager会自己去加载合适的驱动,前提是这个驱动存在于CLASSPATH下

其次,它是怎么加载的呢?为啥Class.forName就能加载呢?

当一个类被加载到JVM时会执行静态代码块,我们以mysql的驱动举例子

java

package com.mysql.cj.jdbc;

import java.sql.DriverManager;
import java.sql.SQLException;

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }

    static {
        try {
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

所以最终调用的还是DriverManager.registerDriver(new Driver());注册一个驱动,底层就是放入到registeredDrivers这个集合中

以瀚高的数据库驱动来看,当调用DriverManager.getDrivers时,DriverManager会去加载驱动类,继而驱动类执行static代码块

Mybatis数据库驱动_第4张图片

Mybatis数据库驱动_第5张图片

最终还是使用DriverManager.registerDriver注册了瀚高的数库驱动

那么回到UnpooledDataSource类中

java

public class UnpooledDataSource implements DataSource {
    private static Map registeredDrivers = new ConcurrentHashMap();
    //.....
    static {
        //这里就会获取到mysql和瀚高的驱动
        Enumeration drivers = DriverManager.getDrivers();
        while (drivers.hasMoreElements()) {
            Driver driver = drivers.nextElement();
            registeredDrivers.put(driver.getClass().getName(), driver);
        }
    }
    //.....
	
    private Connection doGetConnection(Properties properties) throws SQLException {
        initializeDriver();
        Connection connection = DriverManager.getConnection(url, properties);
        configureConnection(connection);
        return connection;
    }
}

initializeDriver()加载一些其他的驱动,例如我们自定义一个类,实现Driver接口,然后在使用

那么Connection connection = DriverManager.getConnection(url, properties);不就是基础的JDBC连接数据库的操作吗

现在还有一个问题,DriverManager是怎么确定使用哪个数据库驱动呢

DriverManager.java

java

private static Connection getConnection(
	//......
    for(DriverInfo aDriver : registeredDrivers) {
        //检查是否能加载这个驱动到jvm,不能就跳过,底层使用Class.forName 没出异常就是能加载
        if(isDriverAllowed(aDriver.driver, callerCL)) {
            try {
                println("    trying " + aDriver.driver.getClass().getName());
                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;
                }
            }

        } else {
            println("    skipping: " + aDriver.getClass().getName());
        }
    }
	//.....
}

底层也很简单,就是遍历驱动集合,每个驱动都去连接一下数据库,如果能连接上说明这个驱动是对的,返回这个驱动创建的连接

也解答了我自己以前的疑惑和错误的理解

  1. 一直不清楚Class.forName("xxx.Driver")是怎么加载驱动的
  2. 以为mybatis配置文件中的 写给哪个环境,哪个环境就使用这个驱动

你可能感兴趣的:(大数据)