Spring Boot + Calcite 实现跨库查询

1. 简介

什么是Calcite?

Apache Calcite 是一个动态数据管理框架。

它包含了典型数据库管理系统(DBMS)的许多核心组件,但省略了一些关键功能:不提供数据存储、不实现数据处理算法,也不维护存储元数据的仓库。

Calcite 刻意避开了数据存储与处理的业务范畴。正如我们将看到的,这使其成为连接应用程序与一个或多个数据存储位置及数据处理引擎的绝佳中介。同时,它也是构建数据库的完美基础------只需补充数据即可。

Calcite应用场景

  • 联邦查询

用一条 SQL 同时查多个异构数据源,如下示例:

sql 复制代码
SELECT u.name, o.amount FROM mysql.users u JOIN csv.orders o ON u.id = o.user_idWHERE o.amount > 200

该示例,将从mysql和csv文件中连接查询数据。

支持的数据源:MySQL、PostgreSQL、Oracle、CSV、JSON、Elasticsearch、Kafka、HDFS 等。

  • 为自研数据库/计算引擎提供 SQL 引擎

避免重复造轮子,快速获得工业级 SQL 能力。

被这些顶级项目采用:Apache Flink:Flink SQL 的解析与优化器; Apache Druid:SQL 查询接口; Splunk:SQL 查询功能底层等。

当你有如下需求时,你可以考虑使用Calcite:

  • 统一查询多个数据源

  • 为产品加 SQL 能力

  • 快速搭建虚拟数据层

本篇文章,我们将基于 MySQL 与 CSV 的跨数据源联合查询实现。

2.实战案例

2.1 引入依赖

xml 复制代码
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
  <groupId>org.apache.calcite</groupId>
  <artifactId>calcite-core</artifactId>
  <version>1.41.0</version>
  <scope>compile</scope>
</dependency>
<dependency>
  <groupId>org.apache.calcite</groupId>
  <artifactId>calcite-file</artifactId>
  <version>1.41.0</version>
  <scope>compile</scope>
</dependency>
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>8.0.33</version>
</dependency>

MySQL数据准备

CSV文件数据

2.2 模型配置

java 复制代码
{
  "version": "1.0",
  "defaultSchema": "users",
  "schemas": [
    {      
     "name": "users",
      "type": "custom",    
      "factory": "org.apache.calcite.adapter.jdbc.JdbcSchema$Factory",
      "operand": {
        "jdbcDriver": "com.mysql.cj.jdbc.Driver",
        "jdbcUrl": "jdbc:mysql://localhost:3306/ddd?serverTimezone=GMT%2B8&useSSL=false&characterEncoding=UTF-8",
        "jdbcUser": "root",
        "jdbcPassword": "root"
          }
    },
    {
      "name": "orders",
      "type": "custom",
      "factory": "org.apache.calcite.adapter.file.FileSchemaFactory",
      "operand": {
        "directory": "F:/datas" // 你csv文件目录,每个文件名将会成为表名
      }
    }
  ]
}

这里我们配置了2个Schema,MySQL和CSV文件。

在准备好上述数据后,我们先使用 Calcite 分别对 MySQL 和 CSV 文件进行查询,以验证数据访问是否正常,最后再将其与 Spring Boot 结合使用。

2.3 基本使用测试

MySQL测试

java 复制代码
@Test
public void testJdbc() throws Exception {
  Properties info = new Properties();  
  info.put("lex", "MYSQL");
  ClassPathResource resource = new ClassPathResource("model.json") ;
  info.put("model", resource.getFile().getAbsolutePath());
  try (Connection connection = DriverManager.getConnection("jdbc:calcite:", info)) {
    Statement statement = connection.createStatement();
    ResultSet rs = statement.executeQuery("select * from users.o_user");
    while (rs.next()) {
      System.err.println("%s, %s, %s, %s, %s".formatted(rs.getObject("id"),rs.getObject("name"),rs.getObject("age"),rs.getObject("sex"),rs.getObject("phone"))) ;
    }
  }
}

运行结果

CSV测试

java 复制代码
@Test
public void testCsv() throws Exception {
  Properties info = new Properties();
  info.put("lex", "MYSQL");
  ClassPathResource resource = new ClassPathResource("model.json") ;
  info.put("model", resource.getFile().getAbsolutePath());
  try (Connection connection = DriverManager.getConnection("jdbc:calcite:", info)) {
    Statement statement = connection.createStatement();
    ResultSet rs = statement.executeQuery("select * from orders.orders");
    while (rs.next()) {
      System.err.println("%s, %s, %s, %s, %s".formatted(rs.getObject("order_id"),rs.getObject("user_id"),rs.getObject("product"),rs.getObject("amount"),rs.getObject("order_time"))) ;
    }
  }
}

运行结果

2.4 MySQL+CSV联合查询

首先,自定义数据源

java 复制代码
public class CalciteDataSource implements DataSource {
  private final Properties info ;
  public CalciteDataSource(String lex, String model) {
    info = new Properties();
    info.put("lex", lex);
    ClassPathResource resource = new ClassPathResource(model) ;
    try {
      info.put("model", resource.getFile().getAbsolutePath());
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
  @Override
  public Connection getConnection() throws SQLException {
    return DriverManager.getConnection("jdbc:calcite:", info) ;
  }
  @Override
  public Connection getConnection(String username, String password) throws SQLException {
    return this.getConnection();
  }
}

接下来,配置数据源

java 复制代码
@Bean(autowireCandidate = false)
DataSource calciteDataSource() throws Exception {
  return new CalciteDataSource("MYSQL", "model.json") ;
}

最后,通过JdbcClient执行联合查询

java 复制代码
@Service
public class CalciteService {
  private final JdbcClient jdbcClient ;
  public CalciteService(ApplicationContext context) {
    this.jdbcClient = JdbcClient.create(context.getBean("calciteDataSource", DataSource.clas)) ;
  }
  public List<Map<String, Object>> queryUser() {
    List<Map<String, Object>> result = this.jdbcClient.sql("select * from users.o_user x left join orders.orders y on(x.id = y.user_id) where x.id = 8 and y.amount > 200") .query().listOfRows() ;
    return result ;
  }
}

运行结果

相关推荐
酷炫码神3 小时前
MosMos 创意效果与能力边界展示
java
Wang's Blog3 小时前
Java 接入Redis: 使用Jedis操作Redis
java·服务器·redis
事圆则缓3 小时前
Java 8 Lambda、Stream、Optional 与 Android 边界:从回调语法到运行时兼容
android·java
shehuiyuelaiyuehao3 小时前
算法50,分治归并,数组中的逆序对
java·算法
烽学长3 小时前
(附源码)基于Springboot+vue的图书阅读分享系统的设计与实现
java·spring boot·后端
Ivanqhz3 小时前
数据搬运是性能关键
java·服务器·网络·人工智能·深度学习
青山木3 小时前
RocketMQ 入门到原理(三):消息存储原理
java·后端·中间件·架构·rocketmq
MayBaymax3 小时前
Spring AI Alibaba Graph 实战:客服工单智能处理
java·ai·ai编程
光依旧4 小时前
MCP实战手记系列(五):让工具返回一个能点的界面(文末附github源码链接)
java·spring ai·前端集成·mcp·mcp apps