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. MySQL(1): 基本操作

    MySQL 是流行的关系型数据库管理系统之一,特别是在WEB应用方面.推荐用5.6版本. My Sql客户端有很多 还有PHP MyAdmin, 是以web形式控制和操作MySQL数据库的管理工具.是 ...

  2. DNS服务器(简)

    服务端:192.168.182.187 客户端:192.168.182.16 windows客户端:192.168.182.17 1.安装相关服务 yum -y install bind bind-c ...

  3. Centos7编译Nginx1.19.0笔记

    下载Nginx安装包 官网下载页:http://nginx.org/en/download.html 终端输入: # 安装依赖yum -y install wget gcc gcc-c++ autoc ...

  4. 应用Sequelize创建项目

    创建项目: 第一步:安装express-generator -g 第二步:安装ejs模板  express --view=ejs 项目名 第三步:安装依赖进入项目 npm i ------------ ...

  5. C#使用JSON相关

    一.Json字符串转换为Dictionary /// <summary> /// JSON字符串转为 Dictionary /// </summary> /// <typ ...

  6. 新安装的eclipse没有新建java project----解决方法:安装插件

    问题描述:最近新安装的一个eclipse版本,建立新工程的时候发现没有java project选项,如下: 百度了一些资料:https://blog.csdn.net/sinat_41752599/a ...

  7. openfire开源IM服务器知识分享+社交app实战

    一.      概述 Openfire最主要的功能是实现XMPP服务器,简单来说,openfire为我们提供一个固定的地址,我们只需要向openfire服务器发送标准的XMPP信息(即XML文件流), ...

  8. Java中int型数据类型对应MySQL数据库中哪种类型?

    java类   mysql数据库 java.lang.Byte byte TINYINT java.lang.Short short SMALLINT java.lang.Integer intege ...

  9. SQL Server 启用“锁定内存页”

    这次在虚拟机中做了一个模拟器做压力测试,简单模拟了一条20个工位的生产线上生产1000个工件,并向 MES 服务端发起功能请求,保存质量数据和扫码数据到数据库.在测试时发现服务端进程的 CPU 占用有 ...

  10. PASS模型需求分析和原型设计

    班级网址 https://edu.cnblogs.com/campus/zjcsxy/SE2020 作业要求 https://edu.cnblogs.com/campus/zjcsxy/SE2020/ ...