前言
MySQl是现在互联网中使用最广泛的数据库。Connector/J则是MySQl为Java程序客户端应用程序提供的JDBC连接方案。
逻辑结构概述
Connector其大致调用逻辑如下图所示
DriverManager
我们最原始的获取Mysql连接的方式如下
Class.forName("com.mysql.cj.jdbc.Driver");
//或者设置环境变量jdbc.drivers让DriverManager帮忙调用Class.forName
//System.setProperty("jdbc.drivers","com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost/test?logger=Slf4JLogger", "sa", "sa");
通过阅读DriverManager的源码可以发现,它是一个Driver管理器,其获取Driver途径主要为registerDriver方法。
- com.mysql.cj.jdbc.Driver 在static模块中调用registerDriver
- DriverManager加载时会解析环境变量jdbc.drivers声明的class,然后加载
- 当执行conn = DriverManager.getConnection进行Driver的选择
private static Connection getConnection(
String url, java.util.Properties info, Class<?> caller) throws SQLException {
....
for(DriverInfo aDriver : registeredDrivers) {
// If the caller does not have permission to load the driver then
// skip it.
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());
}
}
....
}
Driver
根据选择的driver,查看其connect源码,得知Driver只是简单解析驱动管理器的命令,生成不同策略的连接。
@Override
public java.sql.Connection connect(String url, Properties info) throws SQLException {
...
ConnectionUrl conStr = ConnectionUrl.getConnectionUrlInstance(url, info);
//根据不能的协议创建不同策略的Connection
switch (conStr.getType()) {
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;
}
...
}
1. Single:普通的一个连接,提供我们常见的Statement、PreparedStatement一系列的数据操作.其配置如下
jdbc:mysql://ip:port/dbname
2. Loadbalace:底层代理多个Single连接.其配置如下
jdbc:mysql:loadbalance://[host1][:port],[host2][:port][,[host3][:port]]/dbname
通常在遇到通讯异常或者提交/回滚事务的时候才更换连接,它的核心目的有两个:
- 让调用者可以重复使用动态代理连接。
- 以事务为单位的负载均衡。
使用此模式,必须要将多台Mysql数据的一致性放在最首要的位置来考虑(不推荐)
3. Failover:底层代理多个Single连接.其配置如下
jdbc:mysql://[primary host][:port],[secondary host 1][:port]/dbname
底层的连接出状况时候,关闭旧连接,获取新连接,其主要有2种方式:
- 自动切换连接,对事务操作影响及大
- 失败让调用者感知,再调用则切换
使用此模式,必须要将多台Mysql数据的一致性放在最首要的位置来考虑(不推荐)
4. Replication:底层代理多个Single连接.其配置如下
jdbc:mysql:replication://[master host][:port],[slave host 1][:port],[slave host 2][:port]/dbname
底层会建立两组连接,一组负责读操作,一组负责写为主操作.通过设置Connection.setReadOnly(false),驱动将会保证你的请求会到主库。否则,读库。
Connection
不管Driver层选择了何种策略,最终干活的还是ConnectionImpl.
. 1. ConnectionImpl通过解析各咱url后面的参数,具体参数含意请参考《Mysql配置文档》
2. 通过createNewIO,创建Socket与Mysql数据管理软件的数据交互
PS:8版本没有MysqlIO类,用NativeSession了
总结
不要小瞧Connector,他也能做许多事情。关于《Mysql配置文档》与源码相结合。会有更多发现。
比如引入第三方包p6spy,更换Driver,输出sql完整日志
jdbc:p6spy:mysql://ip:port/dbname
<dependency>
<groupid>p6spy</groupid>
<artifactid>p6spy</artifactid>
<version>x.x.x</version>
</dependency>