Apache Calcite JDBC查询流程详解
之前写了一篇文章使用Apache Calcite进行JDBC多数据源关联里面使用Calcite的JDBC的Adapter对后端数据库进行SQL查询,代码例子如下,整个实现过程很简单,与常规的JDBC使用没有任何区别。但是Calcite在内部整个流程中做了很多有趣的工作,比如sql解析,生成关系表达式,优化关系表达式生成执行计划等。这篇文章会详细分析Calcite JDBC查询的整个流程实现,理解了这整个流程就对Calcite的核心功能和Calcite能做什么都会有深入的了解。
@Test
public void singleSourceTest() throws SQLException {
Properties config = new Properties();
config.put("model", TestUtil.resourcePath("singleSource.json"));
config.put("lex", "MYSQL");
String sql = "select s.name,c.name from student as s join colleage as c on s.cid = c.id";
try (Connection con = DriverManager.getConnection("jdbc:calcite:", config)) {
try (Statement stmt = con.createStatement()) {
try (ResultSet rs = stmt.executeQuery(sql)) {
printRs(rs);
}
}
}
}
Calcite JDBC的实现类
Calcite不仅实现了它的核心功能,也为开发者提供了一个称为Avatica的构建JDBC和ODBC Driver的框架,如果希望实现自己的JDBC Driver可以详细学习一下这个框架。
从上图可知,Calcite core模块中的JDBC的类实现都是基于Avatica框架。我们在使用Calcite进行JDBC查询时Connection,Statement和ResultSet实例其实就是CalciteJdbc41Connection,CalciteJdbc41Statement,CalciteResultSet。
Calcite的JDBC Driver
在使用Calcite的JDBC Driver时,我们无需手动注册。如下图所示,Calcite核心模块会利用SPI方式注册。
Avatica框架在创建JDBC的接口实例的时候要使用一个工场类来创建对应的实例。Calcite的Driver实现中通过getFactoryClassName
方法获得这个工场类。另外,在这个方法中也可以看出Calcite JDBC实现只支持JDBC4.1版本。
@Override protected String getFactoryClassName(JdbcVersion jdbcVersion) {
switch (jdbcVersion) {
case JDBC_30:
case JDBC_40:
throw new IllegalArgumentException("JDBC version not supported: "
+ jdbcVersion);
case JDBC_41:
default:
return "org.apache.calcite.jdbc.CalciteJdbc41Factory";
}
}
如下图所示,通过CalciteJdbc41Factory提供的方法,可以获得Connection, Statement, ResultSet等实例对象。
获取Calcite的JDBC Conection
当我们通过DriverManager获取Calcite的Connection的时候,大致过程如下:
DriverManager会调用Calcite的Driver的connect
方法来创建Calcite Connection实例:
public Connection connect(String url, Properties info) throws SQLException {
if (!acceptsURL(url)) {
return null;
}
final String prefix = getConnectStringPrefix();
assert url.startsWith(prefix);
final String urlSuffix = url.substring(prefix.length());
final Properties info2 = ConnectStringParser.parse(urlSuffix, info);
final AvaticaConnection connection =
factory.newConnection(this, factory, url, info2);
handler.onConnectionInit(connection);
return connection;
}
上面的connect方法实现中,Calcite的Connection是通过前面说的CalciteJdbc41Factory创建的,Connection实例的类是CalciteJdbc41Connection。
另外在返回Connection之前,Calcite还会根据配置的model来解析model格式(json或yaml)生成JdbcSchema。
{
"defaultSchema": "db1",
"schemas": [
{
"factory": "org.apache.calcite.adapter.jdbc.JdbcSchema$Factory",
"name": "db1",
"operand": {
"jdbcDriver": "com.mysql.cj.jdbc.Driver",
"jdbcPassword": "changeme",
"jdbcUrl": "jdbc:mysql://localhost:3306/test",
"jdbcUser": "root"
},
"type": "custom"
}
],
"version": "1.0"
}
JdbcSchema在创建的过程主要是根据model中jdbc的信息建立与后端数据库的连接池。
创建Statement
当我们调用connection的createStatement
方法创建Statement实例的时候,Calcite的执行流程如上图。整