首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

JSON 序列化巢狀階層資料 (國家-省份-城市) 的資料庫設計與模型映射

     編輯:程式設計綜合問答

目標 JSON 結構

[
  {
    "CountryName": "阿拉伯聯合酋長國",
    "Province": [
      {
        "ProvinceName": "Bahrani",
        "City": [ {"CityName": "迪拜"}, {"CityName": "沙迦"}, {"CityName": "拉斯海馬"} ]
      },
      {
        "ProvinceName": "阿布扎比",
        "City": [ {"CityName": "阿布扎比"} ]
      }
    ]
  },
  ...
]

資料庫設計方案

方案一:三表正規化 (標準關聯式設計)

CREATE TABLE Country (
    Id INT PRIMARY KEY AUTO_INCREMENT,
    Name VARCHAR(100) NOT NULL
);

CREATE TABLE Province (
    Id INT PRIMARY KEY AUTO_INCREMENT,
    CountryId INT NOT NULL,
    Name VARCHAR(100) NOT NULL,
    FOREIGN KEY (CountryId) REFERENCES Country(Id)
);

CREATE TABLE City (
    Id INT PRIMARY KEY AUTO_INCREMENT,
    ProvinceId INT NOT NULL,
    Name VARCHAR(100) NOT NULL,
    FOREIGN KEY (ProvinceId) REFERENCES Province(Id)
);

查詢策略:

  • 單次查詢 `JOIN` 三表取得扁平資料,Java 端記憶體中組裝樹狀結構 (推薦)。
  • 或分三次查詢 (Country → Province → City),利用 `Map` 組裝。

方案二:JSON 欄位儲存 (PostgreSQL/MySQL 5.7+/SQL Server 2016+)

CREATE TABLE RegionTree (
    Id INT PRIMARY KEY,
    Data JSON NOT NULL  -- 直接儲存整棵 JSON
);

優點:讀取極快、無需 Join、結構彈性。
缺點:查詢/更新單一城市困難、無外鍵約束、索引支援有限。

Java 實體類與序列化 (Jackson/Gson)


public class Country {
    private String countryName;
    private List province;  // JSON key "Province"
    // getter/setter
}

public class Province {
    private String provinceName;
    private List city;  // JSON key "City"
    // getter/setter
}

public class City {
    private String cityName;
    // getter/setter
}

組裝邏輯 (方案一:扁平資料 → 樹狀物件)


// 假設查詢得到扁平 DTO 列表
List flatList = jdbcTemplate.query(sql, new FlatRegionRowMapper());

// 使用 Map 依 ID 分組組裝
Map countryMap = new LinkedHashMap<>();
for (FlatRegion f : flatList) {
    Country c = countryMap.computeIfAbsent(f.getCountryId(), id -> {
        Country nc = new Country();
        nc.setCountryName(f.getCountryName());
        nc.setProvince(new ArrayList<>());
        return nc;
    });
    
    Province p = c.getProvince().stream()
        .filter(prov -> prov.getProvinceName().equals(f.getProvinceName()))
        .findFirst()
        .orElseGet(() -> {
            Province np = new Province();
            np.setProvinceName(f.getProvinceName());
            np.setCity(new ArrayList<>());
            c.getProvince().add(np);
            return np;
        });
    
    p.getCity().add(new City(f.getCityName()));
}

List result = new ArrayList<>(countryMap.values());
// Jackson 序列化
String json = new ObjectMapper().writeValueAsString(result);

第三方庫直接序列化 ResultSet (不建議複雜結構)

若堅持單表/單查詢直接序列化,需自訂 `ResultSetExtractor` 或使用 MyBatis `resultMap` 的 `collection`/`association` 嵌套映射,但效能與維護性通常不如「查扁平 + 記憶體組裝」。

結論

標準做法:三表正規化 + 扁平 JOIN 查詢 + Java 端 Map 組裝樹狀結構 + Jackson 序列化。兼顧資料完整性、查詢效能、程式碼可維護性。若資料極少變動且結構固定,JSON 欄位儲存也是可行選項。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved