Mybatis数据库驱动

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

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

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency> <dependency>
<groupId>com.highgo</groupId>
<artifactId>HgdbJdbc</artifactId>
<version>6.2.2</version>
</dependency>

主启动类.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<User> users = mapper.getUsers(1);
session.close();
}
}

User.java


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

UserMapper.java

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

UserMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.mybatis.mapper.UserMapper" > <select id="getUsers" resultType="org.mybatis.pojo.User" databaseId="mysql">
select * from user where age = #{age}
</select> <select id="getUsers" resultType="org.mybatis.pojo.User" databaseId="postgresql">
select * from mybatis.user where age = #{age}
</select> </mapper>

Mybatis配置文件

<configuration>
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment> <environment id="highgo">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:highgo://localhost:5866/highgo?currentSchema=mybatis"/>
<property name="username" value="highgo"/>
<property name="password" value="Hello@123"/>
</dataSource>
</environment>
</environments> <databaseIdProvider type="DB_VENDOR">
<property name="MySQL" value="mysql"/>
<property name="PostgreSQL" value="postgresql"/>
</databaseIdProvider> <mappers>
<package name="org.mybatis.mapper"/>
</mappers> </configuration>

当我把mybatis配置文件中的环境设置为<environments default="mysql">,代码执行结果如下

然后我修改环境设置为<environments default="highgo">后,代码执行结果如下

不知道您有没有看出问题所在,在上面的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

private Connection doGetConnection(Properties properties) throws SQLException {
initializeDriver();
Connection connection = DriverManager.getConnection(url, properties);
configureConnection(connection);
return connection;
}
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);
}
}
}

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

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

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

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

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

public class DriverManager {

    // List of registered JDBC drivers
private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
//....
public static java.util.Enumeration<Driver> getDrivers() {
java.util.Vector<Driver> 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时,肯定都写过类似这样的代码

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的驱动举例子

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.registerDriver注册了瀚高的数库驱动

那么回到UnpooledDataSource类中

public class UnpooledDataSource implements DataSource {
private static Map<String, Driver> registeredDrivers = new ConcurrentHashMap<String, Driver>();
//.....
static {
//这里就会获取到mysql和瀚高的驱动
Enumeration<Driver> 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接口,然后在<property name="driver" value="com.drive.MyDrive"/>使用

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

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

  1. 一直不清楚Class.forName("")的作用
  2. 以为mybatis配置文件中的<property name="driver" value="com.mysql.cj.jdbc.Driver"/> 写给那个环境,那个环境就使用这个驱动

现在是明白了

Mybatis数据库驱动的更多相关文章

  1. 170717、springboot编程之mybatis数据库开发和aop拦截

    一.springboot整合mybaits (1)新建maven project; 新建一个maven project,取名为:spring-boot-mybatis (2)在pom.xml文件中引入 ...

  2. JDBC 学习笔记(四)—— JDBC 加载数据库驱动,获取数据库连接

    1. 加载数据库驱动 通常来说,JDBC 使用 Class 类的 forName() 静态方法来加载驱动,需要输入数据库驱动代表的字符串. 例如: 加载 MySQL 驱动: Class.forName ...

  3. Java-加载数据库驱动,取得数据库连接

    在Java中想要进行数据库操作,最重要的两个步骤就是加载数据驱动,然后取得数据库连接. 1.加载 数据库驱动( Class.forName(String className) ): 因为Java是一种 ...

  4. 关于iBatis.NET连接各数据库时提示没找到数据库驱动的依赖文件

    iBatis.net在连接oracle数据库时使用的是:oracleClient1.0 这个是系统自带的驱动,配置上即可,使用的连接配置为: <database> <provider ...

  5. Eclipse连接到My sql数据库的操作总结/配置数据库驱动

    Eclipse连接到MYSQL数据库的操作 (自己亲测,开始学习Eclipse(我的Eclipse版本是4.5.2,Jdbc驱动器的jar包版本是5.1.7,亲测可以使用)连接到数据库的时候,发现网上 ...

  6. PHP 数据库驱动、连接数据不同方式学习笔记

    相关学习资料 http://www.php.net/manual/zh/refs.database.php http://www.php.net/manual/zh/internals2.pdo.ph ...

  7. Class.forName()数据库驱动

    在学习jdbc中,用到Class.forName(驱动);,当时学习的时候知道Class.forName就是加载一个类到虚拟机,在加载一个类的时候,这个类的信息会被放到一个方法区,一个CLass 在J ...

  8. 解析Qt4.7.3编译MySql数据库驱动,存中文乱码、过滤转义字符问题

    问题:使用QSqlDataBase类建立连接MySql数据库驱动加载失败 QSqlDatabase: QMYSQL driver not loaded QSqlDatabase: available ...

  9. MyGeneration 默认设置中没有数据库驱动

    这 个问题的出现基本上是因为MyGeneration 1.3需要的是 .Net framework 4.0,如果系统安装了 .Net 2.0的版本,安装程序执行的 regasm.exe为2.0版本下的 ...

  10. JDBC加载数据库驱动的方式

    JDBC作为数据库访问的规范接口,其中只是定义一些接口.具体的实现是由各个数据库厂商来完成. 一.重要的接口: 1.public interface Driver 每个驱动程序类必须实现的接口.Jav ...

随机推荐

  1. Html5本地存储概念,有什么优点,与cookie有什么区别?

    html5中的Web Storage包括了两种存储方式:sessionStorage和localStorage sessionStorage用于本地存储一个会话(session)中的数据,这些数据只有 ...

  2. servlet 500,405错误

    servlet500错误 状况: 1.能打开项目,但是打开web.xml中指定的servlet url错误报错500,再次刷新出现404 出现提示: 实例化Servlet类[xxx]异常 java.l ...

  3. TypeScript系列 -> 遇到报错 Cannot find name ‘console‘. Do you need to change your target library?ging the ‘lib‘ compiler option

    学习ts遇到的报错 Cannot find name 'console'. Do you need to change your target library?ging the 'lib' compi ...

  4. 【LeetCode - 1055】形成字符串的最短路径

    1.题目描述 代码: #include <iostream> #include <string> using namespace std; const int MAX_LETT ...

  5. 安卓10.0蓝牙HIDL的直通式初始化流程

    本文仅介绍扼要的流程,没有系统化介绍. 首先从system\bt\hci\src\hci_layer_android.cc文件的函数void hci_initialize() 开始初始化: void ...

  6. CDH命令

    1.检查http服务是否开启 systemctl status httpd.service 本次没有开启是因为删了Log日志 通过查看http状态发现里面有个引用问题 重新建一个空的Log文件夹重启服 ...

  7. Tesstwo9.1.0配置步骤

    一,配置步骤 环境:Tesstwo9.1.0+Android10(华为)+Android11(模拟器) 1.查看tess-two的最新版本(GitHub - rmtheis/tess-two: For ...

  8. 【摘】python和它的装饰器

    python和它的装饰器 即使你没听过装饰器,你也一定知道@staticmethod 作为函数的装饰器,你可以这样写 def mydecorator(function): def wrapped(*a ...

  9. Ext中数据表格序号超过3位数时显示省略号

    问题 在老项目上优化时,遇到了Ext中ColumnModel序号超过3位数时,自动显示了省略号,不友好 修改方法 给RowNumberer添加样式 new Ext.grid.RowNumberer({ ...

  10. 转型.netCore开发 记录日常问题

    1.页面问题 asp-for date字段页面显示时间为"yyyy-MM-dd"类型通过@{}来处理,定义常量,将处理后的值赋值给常量,再通过@常量正常赋值例:@{ var beg ...