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>
項目結構預覽
├── resources/
│ ├── model.json # 本地 JSON 數據
│ └── json-model.schema.json # Calcite 模型 schema
├── controller/
│ └── CalciteQueryController.java
├── service/
│ └── CalciteQueryService.java
├── config/
│ └── CalciteUtils.java # Calcite 配置和執行類
JSON 數據樣例 model.json
[
{"id":1,"name":"Alice","age":30},
{"id":2,"name":"Bob","age":25},
{"id":3,"name":"Charlie","age":28}
]
創建 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"
}
}
]
}
]
}
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;
}
}
查詢接口實現
CalciteQueryService.java
@Service
public class CalciteQueryService {
public List<Map<String, Object>> query(String sql) throws Exception {
return CalciteUtils.executeQuery(sql);
}
}
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());
}
}
}
測試
啟動項目后,訪問:
http://localhost:8080/calcite/query?sql=SELECT * FROM json_schema.model WHERE age > 25
返回結果:
[
{"id":1,"name":"Alice","age":30},
{"id":3,"name":"Charlie","age":28}
]
結語
Apache Calcite 作為一個高度可擴展的查詢引擎,在大數據和異構數據訪問場景中具有廣泛的應用價值。它不僅支持靈活的 SQL 查詢語法,還能與各類數據源輕松集成,讓開發者能夠以統一方式訪問結構化和非結構化數據。
通過本篇文章我們學習了如何結合 Spring Boot3.3 利用 Calcite 查詢本地 JSON 文件,實現了無需轉換即可用 SQL 查詢 JSON 的能力,為復雜的數據分析與快速原型開發提供了極大的便利。
未來你還可以嘗試對接 Elasticsearch、MongoDB、CSV 文件等更多數據源,構建統一查詢平臺,充分發揮 Calcite 的潛能。