不再手搓 JSON:MySQL 9.7 Duality Views 让关系数据与文档双向打通

2026-06-09 16 预计阅读时间: 1 分钟
来源: blogs.oracle.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:9 分钟

现代应用和数据库之间,JSON 是最常见的"运输格式"。MySQL 的 JSON 函数支持一直不错——JSON_OBJECT()JSON_ARRAYAGG()JSON_EXTRACT() 之类的工具随手可用。但问题在于:方向是单向的,且全靠手工拼接。

查数据时,你得用一堆函数把行转成文档;写数据时,又得把 JSON 一层层拆开,逐列逐表写回。这种"JSON 管道工"活儿,写一次嫌烦,维护更痛苦。MySQL 9.7 Community 引入的 Duality Views,正是来终结这种重复劳动的。

关系与文档的"两界分治"困局

先看一个典型场景:订单系统里,orders 表存主信息,order_items 表存明细行。前端要的是一份嵌套 JSON:

{
  "order_id": 1024,
  "customer": "张三",
  "status": "shipped",
  "items": [
    {"sku": "A001", "qty": 2, "price": 15.0},
    {"sku": "B020", "qty": 1, "price": 42.5}
  ]
}

旧做法——出库方向:

SELECT JSON_OBJECT(
  'order_id', o.id,
  'customer', o.customer,
  'status', o.status,
  'items', (
    SELECT JSON_ARRAYAGG(
      JSON_OBJECT('sku', i.sku, 'qty', i.qty, 'price', i.price)
    ) FROM order_items i WHERE i.order_id = o.id
  )
) AS doc
FROM orders o WHERE o.id = 1024;

能跑,但嵌套越深、SQL 越丑。加一层关联就得加一层子查询和聚合。

旧做法——入库方向:

前端 PATCH 回来一段 JSON,你得拆:

-- 先更新主表
UPDATE orders SET status = JSON_UNQUOTE(JSON_EXTRACT(@payload, '$.status'))
WHERE id = JSON_EXTRACT(@payload, '$.order_id');

-- 再逐行处理明细……更麻烦

字段多了就是灾难。每加一个属性,就要多写一条 JSON_EXTRACT,还要处理嵌套数组的遍历与逐行 INSERT/UPDATE/DELETE。

Duality Views:一份定义,双向通行

Duality View 的核心思路很简单:你声明一次"关系→JSON"的映射规则,MySQL 自动帮你做双向转换。 查视图,拿到 JSON 文档;往视图写 JSON,MySQL 自动拆回底层表。

创建一个 Duality View

以下示例基于 MySQL 9.7 的语法。假设已有 ordersorder_items 两张表:

-- 1. 先确保底层表存在
CREATE TABLE IF NOT EXISTS orders (
  id         INT PRIMARY KEY AUTO_INCREMENT,
  customer   VARCHAR(100) NOT NULL,
  status     VARCHAR(20) DEFAULT 'pending',
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE IF NOT EXISTS order_items (
  id        INT PRIMARY KEY AUTO_INCREMENT,
  order_id  INT NOT NULL,
  sku       VARCHAR(50) NOT NULL,
  qty       INT NOT NULL DEFAULT 1,
  price     DECIMAL(10,2) NOT NULL,
  FOREIGN KEY (order_id) REFERENCES orders(id)
);

-- 2. 创建 Duality View:声明 JSON 文档结构
CREATE OR REPLACE DUALITY VIEW order_doc AS
  SELECT JSON_OBJECT(
    'order_id', orders.id,
    'customer', orders.customer,
    'status',   orders.status,
    'items',    JSON_ARRAY(
      SELECT JSON_OBJECT(
        'sku',   order_items.sku,
        'qty',   order_items.qty,
        'price', order_items.price
      )
      FROM order_items
      WHERE order_items.order_id = orders.id
    )
  )
  FROM orders;

这条语句告诉 MySQL:order_doc 视图对外呈现为上述 JSON 结构,内部由 ordersorder_items 两张表支撑。

查:直接拿到完整文档

SELECT * FROM order_doc WHERE order_id = 1024;

返回的就是前端想要的嵌套 JSON,不需要再手写 JSON_ARRAYAGG 拼接。

写:用 JSON 更新底层关系表

假设前端发来一段更新:

-- 通过视图直接 INSERT 一份新订单 JSON
INSERT INTO order_doc VALUES (
  JSON_OBJECT(
    'customer', '李四',
    'status',   'new',
    'items',    JSON_ARRAY(
      JSON_OBJECT('sku', 'C100', 'qty', 3, 'price', 9.99),
      JSON_OBJECT('sku', 'D200', 'qty', 1, 'price', 29.5)
    )
  )
);

MySQL 会自动拆解这份 JSON:主信息写入 orders 表,明细行展开写入 order_items 表,外键关联自动维护。你不再需要手动拆字段、写两条 INSERT。

更新也一样:

UPDATE order_doc
SET doc = JSON_SET(doc, '$.status', 'shipped')
WHERE order_id = 1024;

这条语句只改了 JSON 中的 status 字段,MySQL 知道它映射到 orders.status,于是只更新那一列,不动其他数据。

不只是省代码——一致性也跟着来了

手写 JSON 拆解逻辑时,最容易出的问题不是麻烦,而是不一致

  • 更新主表忘了同步删明细行,数据就脏了。
  • 并发 PATCH 同一份 JSON,两个事务各拆各的,冲突难检测。

Duality View 在底层仍然走 MySQL 的行级锁和事务机制。你对视图做 UPDATE,MySQL 会锁定涉及的 orders 行和对应 order_items 行,事务隔离级别照常生效。这比应用层"读 JSON→拆→写多表"的裸操作安全得多。

另外,视图定义本身就是一份显式的映射契约。哪个 JSON 字段对应哪张表的哪列,一目了然。后来人不需要去业务代码里翻"到底哪个字段写进了哪张表"。

实践建议与边界

什么时候该用

  • API 后端直接对接前端 JSON——REST/GraphQL 返回嵌套文档、接收 PATCH,是最典型的场景。
  • 多表聚合输出——订单+明细、用户+偏好、文章+标签,凡是"一主多从"的嵌套结构,Duality View 都能省掉大量拼接代码。
  • 快速原型——新项目不想先写 ORM 映射,用视图直接暴露 JSON,后期再优化。

什么时候要谨慎

  • 深层嵌套(3层以上)——Duality View 目前对多层嵌套的支持有性能边界,三层以上的父子关联可能不如手写 JOIN + 应用层组装灵活。
  • 高频批量写入——每条 INSERT/UPDATE 都要经过 JSON→关系的拆解路径,比直接写表多一层开销。批量导入场景还是直接 LOAD DATA 更快。
  • 部分更新粒度控制——JSON_SET 只改一个字段很方便,但如果业务要求"只有特定角色能改特定字段",视图本身不做字段级权限,仍需在应用层或列级权限补齐。

迁移路径

如果你已有手写的 JSON 拼装逻辑,可以这样逐步替换:

  1. 先只替换读方向——创建 Duality View,让 SELECT 走视图,验证输出 JSON 和旧逻辑一致。
  2. 再替换写方向——把 INSERT/UPDATE 从"拆 JSON 写多表"改为走视图,逐接口切换。
  3. 最后清理旧代码——删掉应用层的 JSON 拆装函数,视图定义成为唯一映射源。
# 快速检查当前 MySQL 版本是否支持
mysql -u root -p -e "SELECT VERSION();"
# 需要 9.7.0+ Community 版本

# 查看已有 Duality View 定义
mysql -u root -p -e "SHOW CREATE DUALITY VIEW order_doc;" your_db

Duality Views 不是让 MySQL 变成 MongoDB——底层仍然是关系表,索引、约束、事务照常工作。它做的是消除关系与文档之间的手工翻译层,让开发者不再在 SQL 里堆 JSON_EXTRACT,也不在应用代码里写拆解逻辑。MySQL 9.7 Community 把这个能力从 HeatWave 云版下放到社区版,对大多数 Web 后端来说,这是一个值得立刻试用的特性。


相关推荐