Spring Boot3.3 + Apache Calcite 實戰(zhàn):用 SQL 動態(tài)查詢 JSON 數(shù)據(jù)源
在現(xiàn)代 Web 應(yīng)用開發(fā)中,數(shù)據(jù)來源日益多樣化,除了傳統(tǒng)的關(guān)系型數(shù)據(jù)庫,JSON 文件、NoSQL 以及各種 API 接口提供的數(shù)據(jù)格式也被廣泛使用。為了以統(tǒng)一的方式對異構(gòu)數(shù)據(jù)源進行查詢,Apache Calcite 提供了強大且靈活的 SQL 查詢引擎,可以通過虛擬視圖將 JSON 數(shù)據(jù)建模為表結(jié)構(gòu),再用標準 SQL 語句進行高效查詢。
本篇文章將結(jié)合 Spring Boot3.3 與 Apache Calcite,詳細講解如何將本地 JSON 文件作為數(shù)據(jù)源,通過 Calcite 提供的 schema 映射機制進行 SQL 查詢操作,幫助你輕松實現(xiàn)對 JSON 數(shù)據(jù)的結(jié)構(gòu)化訪問與動態(tài)查詢。
Apache Calcite 簡介
Apache Calcite 是一個開源的動態(tài)數(shù)據(jù)管理框架,它本身并不是數(shù)據(jù)庫,而是一個提供 SQL 查詢解析、驗證、優(yōu)化和執(zhí)行的中間層框架,支持對多種數(shù)據(jù)源進行統(tǒng)一訪問。其核心特性包括:
- 多數(shù)據(jù)源支持支持關(guān)系型數(shù)據(jù)庫、CSV、JSON、MongoDB、Elasticsearch 等多種數(shù)據(jù)源。
 - SQL 引擎具備完整的 SQL 解析、校驗、優(yōu)化和執(zhí)行能力。
 - 虛擬化查詢可將非結(jié)構(gòu)化或半結(jié)構(gòu)化數(shù)據(jù)通過 schema 建模為結(jié)構(gòu)化視圖。
 - 可插拔架構(gòu)支持自定義函數(shù)、自定義規(guī)則、插件式架構(gòu)。
 
在本文中,我們將使用 Apache Calcite 對本地 JSON 文件進行虛擬化建模,并通過標準 SQL 查詢訪問數(shù)據(jù)。
項目依賴配置(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>項目結(jié)構(gòu)預(yù)覽
├── resources/
│   ├── model.json                  # 本地 JSON 數(shù)據(jù)
│   └── json-model.schema.json     # Calcite 模型 schema
├── controller/
│   └── CalciteQueryController.java
├── service/
│   └── CalciteQueryService.java
├── config/
│   └── CalciteUtils.java          # Calcite 配置和執(zhí)行類JSON 數(shù)據(jù)樣例 model.json
[
  {"id":1,"name":"Alice","age":30},
{"id":2,"name":"Bob","age":25},
{"id":3,"name":"Charlie","age":28}
]創(chuàng)建 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;
    }
}查詢接口實現(xiàn)
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返回結(jié)果:
[
  {"id":1,"name":"Alice","age":30},
{"id":3,"name":"Charlie","age":28}
]結(jié)語
Apache Calcite 作為一個高度可擴展的查詢引擎,在大數(shù)據(jù)和異構(gòu)數(shù)據(jù)訪問場景中具有廣泛的應(yīng)用價值。它不僅支持靈活的 SQL 查詢語法,還能與各類數(shù)據(jù)源輕松集成,讓開發(fā)者能夠以統(tǒng)一方式訪問結(jié)構(gòu)化和非結(jié)構(gòu)化數(shù)據(jù)。
通過本篇文章我們學(xué)習(xí)了如何結(jié)合 Spring Boot3.3 利用 Calcite 查詢本地 JSON 文件,實現(xiàn)了無需轉(zhuǎn)換即可用 SQL 查詢 JSON 的能力,為復(fù)雜的數(shù)據(jù)分析與快速原型開發(fā)提供了極大的便利。
未來你還可以嘗試對接 Elasticsearch、MongoDB、CSV 文件等更多數(shù)據(jù)源,構(gòu)建統(tǒng)一查詢平臺,充分發(fā)揮 Calcite 的潛能。















 
 
 














 
 
 
 