Spring Boot3.3 + Apache Calcite 实战:用 SQL 动态查询 JSON 数据源

在现代 Web 应用开发中,数据来源日益多样化,除了传统的关系型数据库,JSON 文件、NoSQL 以及各种 API 接口提供的数据格式也被广泛使用。为了以统一的方式对异构数据源进行查询,Apache Calcite 提供了强大且灵活的 SQL 查询引擎,可以通过虚拟视图将 JSON 数据建模为表结构,再用标准 SQL 语句进行高效查询。

本篇文章将结合 Spring Boot3.3 与 Apache Calcite,详细讲解如何将本地 JSON 文件作为数据源,通过 Calcite 提供的 schema 映射机制进行 SQL 查询操作,帮助你轻松实现对 JSON 数据的结构化访问与动态查询。

Apache Calcite 简介

Apache Calcite 是一个开源的动态数据管理框架,它本身并不是数据库,而是一个提供 SQL 查询解析、验证、优化和执行的中间层框架,支持对多种数据源进行统一访问。其核心特性包括:

多数据源支持支持关系型数据库、CSV、JSON、MongoDB、Elasticsearch 等多种数据源。SQL 引擎具备完整的 SQL 解析、校验、优化和执行能力。虚拟化查询可将非结构化或半结构化数据通过 schema 建模为结构化视图。可插拔架构支持自定义函数、自定义规则、插件式架构。

在本文中,我们将使用 Apache Calcite 对本地 JSON 文件进行虚拟化建模,并通过标准 SQL 查询访问数据。

项目依赖配置(pom.xml)
复制
<dependencies> <!-- Apache Calcite 核心依赖 --> <dependency> <groupId>org.apache.calcite</groupId> <artifactId>calcite-core</artifactId> <version>1.35.0</version> </dependency> <!-- Jackson 用于 JSON 解析 --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> <!-- Spring Boot Web 模块 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.
项目结构预览
复制
├── resources/ │ ├── model.json # 本地 JSON 数据 │ └── json-model.schema.json # Calcite 模型 schema ├── controller/ │ └── CalciteQueryController.java ├── service/ │ └── CalciteQueryService.java ├── config/ │ └── CalciteUtils.java # Calcite 配置和执行类1.2.3.4.5.6.7.8.9.
JSON 数据样例 model.json
复制
[ {"id":1,"name":"Alice","age":30}, {"id":2,"name":"Bob","age":25}, {"id":3,"name":"Charlie","age":28} ]1.2.3.4.5.
创建 Calcite 模型文件 json-model.schema.json
复制
{ "version":"1.0", "defaultSchema":"json_schema", "schemas":[ { "name":"json_schema", "type":"custom", "factory":"org.apache.calcite.adapter.json.JsonSchemaFactory", "operand":{ "directory":"src/main/resources", "flavor":"file" }, "tables":[ { "name":"model", "type":"custom", "factory":"org.apache.calcite.adapter.json.JsonTableFactory", "operand":{ "path":"model.json" } } ] } ] }1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.
Calcite 工具类 CalciteUtils.java
复制
@Slf4j public class CalciteUtils { public static Connection getConnection() throws Exception { Properties info = new Properties(); try { String modelPath = Paths.get("src/main/resources/json-model.schema.json").toAbsolutePath().toString(); info.put("model", modelPath); return DriverManager.getConnection("jdbc:calcite:", info); } catch (Exception e) { log.error("初始化 Calcite 连接失败", e); throw e; } } public static List<Map<String, Object>> executeQuery(String sql) throws Exception { List<Map<String, Object>> results = new ArrayList<>(); try (Connection conn = getConnection(); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql)) { ResultSetMetaData meta = rs.getMetaData(); int columnCount = meta.getColumnCount(); while (rs.next()) { Map<String, Object> row = new LinkedHashMap<>(); for (int i = 1; i <= columnCount; i++) { row.put(meta.getColumnLabel(i), rs.getObject(i)); } results.add(row); } } return results; } }1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.
查询接口实现

CalciteQueryService.java

复制
@Service public class CalciteQueryService { public List<Map<String, Object>> query(String sql) throws Exception { return CalciteUtils.executeQuery(sql); } }1.2.3.4.5.6.

CalciteQueryController.java

复制
@RestController @RequestMapping("/calcite") public class CalciteQueryController { @Autowired private CalciteQueryService service; @GetMapping("/query") public ResponseEntity<?> query(@RequestParam String sql) { try { return ResponseEntity.ok(service.query(sql)); } catch (Exception e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage()); } } }1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.
测试

启动项目后,访问:

复制
http://localhost:8080/calcite/query?sql=SELECT * FROM json_schema.model WHERE age > 251.

返回结果:

复制
[ {"id":1,"name":"Alice","age":30}, {"id":3,"name":"Charlie","age":28} ]1.2.3.4.

结语

Apache Calcite 作为一个高度可扩展的查询引擎,在大数据和异构数据访问场景中具有广泛的应用价值。它不仅支持灵活的 SQL 查询语法,还能与各类数据源轻松集成,让开发者能够以统一方式访问结构化和非结构化数据。

通过本篇文章我们学习了如何结合 Spring Boot3.3 利用 Calcite 查询本地 JSON 文件,实现了无需转换即可用 SQL 查询 JSON 的能力,为复杂的数据分析与快速原型开发提供了极大的便利。

未来你还可以尝试对接 Elasticsearch、MongoDB、CSV 文件等更多数据源,构建统一查询平台,充分发挥 Calcite 的潜能。

阅读剩余
THE END