引言
随着现代应用对灵活数据结构的需求日益增长,主流关系型数据库(如 PostgreSQL、MySQL 5.7+、SQL Server 2016+)纷纷引入了对 JSON 类型列 的原生支持。开发者可以在保持关系模型优势的同时,嵌入半结构化数据,实现“关系 + 文档”的混合存储模式。
作为 .NET 生态中功能强大且轻量的 ORM 框架,FreeSql 不仅全面支持主流数据库,还针对 JSON 列提供了优雅、高性能的操作能力。本文将深入剖析 FreeSql 如何实现 JSON 列的映射、查询、更新与索引优化,并通过实战示例展示其在真实场景中的应用价值。
一、FreeSql 中 JSON 列的基本定义
FreeSql 通过 [Column(DbType = "json")] 或 [Column(IsJson = true)] 特性,将 C# 对象属性映射为数据库的 JSON 列。
public class User
{
public long Id { get; set; }
public string Name { get; set; }
[Column(IsJson = true)]
public Address Address { get; set; } // 自动序列化为 JSON
}
public class Address
{
public string City { get; set; }
public string Street { get; set; }
public int ZipCode { get; set; }
}
当执行 Insert 或 Update 时,FreeSql 会自动使用 System.Text.Json(或可配置为 Newtonsoft.Json)将 Address 对象序列化为 JSON 字符串存入数据库;读取时则自动反序列化回对象。
✅ 支持嵌套对象、列表、字典等复杂结构。
二、跨数据库兼容性处理
不同数据库对 JSON 的语法支持存在差异,FreeSql 通过 表达式解析器 和 SQL 生成器 抽象层,实现了统一的 LINQ 查询接口:
| 数据库 | JSON 查询函数示例 |
|---|---|
| MySQL | JSON_EXTRACT(Address, '$.City') |
| PostgreSQL | (Address->>'City') |
| SQL Server | JSON_VALUE(Address, '$.City') |
| SQLite(模拟) | 使用字符串函数模拟(需开启扩展) |
FreeSql 在生成 SQL 时会根据当前 IFreeSql 实例的数据库类型,自动选择正确的 JSON 函数。
三、JSON 列的查询:LINQ 与 Lambda 表达式
FreeSql 允许直接在 LINQ 中访问 JSON 对象的属性,框架会将其转换为对应的数据库 JSON 查询语句。
示例:查询居住在北京的用户
var users = fsql.Select<User>()
.Where(u => u.Address.City == "北京")
.ToList();
生成的 SQL(以 MySQL 为例):
SELECT * FROM `User`
WHERE JSON_EXTRACT(`Address`, '$.City') = '北京'
支持复杂条件与嵌套路径
// 查询邮编大于 100000 的用户
.Where(u => u.Address.ZipCode > 100000)
// 若 Address 是 List<Address>
.Where(u => u.Addresses.Any(a => a.City == "上海"))
🔍 注意:
Any、All等方法在 JSON 数组场景下会生成JSON_CONTAINS或EXISTS子查询(依数据库而定)。
四、JSON 列的更新:局部修改 vs 整体替换
方案 1:整体更新(默认)
user.Address.Street = "中关村大街1号";
fsql.Update<User>()
.SetSource(user)
.ExecuteAffrows();
此方式会重新序列化整个 Address 对象并覆盖原 JSON 列。
方案 2:局部更新(推荐用于大对象)
FreeSql 提供 Set() 方法结合表达式,实现字段级更新:
fsql.Update<User>()
.Set(u => u.Address.Street, "五道口")
.Where(u => u.Id == 1)
.ExecuteAffrows();
生成的 SQL(PostgreSQL):
UPDATE "User"
SET "Address" = jsonb_set("Address", '{Street}', '"五道口"')
WHERE "Id" = 1
✅ 局部更新避免传输和解析整个 JSON,显著提升性能,尤其适用于大型配置对象。
五、性能优化建议
-
添加 JSON 路径索引
在高频查询的 JSON 字段路径上创建函数索引:- MySQL:
ALTER TABLE User ADD INDEX idx_city ((CAST(Address->'$.City' AS CHAR(20)))) - PostgreSQL:
CREATE INDEX idx_user_city ON User ((Address->>'City'))
- MySQL:
-
避免过度嵌套
过深的 JSON 结构会降低查询效率,建议将高频查询字段提升为独立列。 -
启用压缩(可选)
对于超大 JSON(如日志、配置快照),可考虑在应用层 GZip 压缩后存为byte[],但会丧失数据库内查询能力。
六、局限性与注意事项
- SQLite 默认不支持 JSON:需加载
json1扩展,且功能有限; - 类型安全依赖编译时信息:运行时动态 JSON 结构需使用
Dictionary<string, object>或JsonDocument; - 部分数据库不支持 JSON 数组的高效遍历:复杂查询可能需回退到应用层过滤。
结语
FreeSql 对 JSON 列的支持,不仅简化了半结构化数据的持久化操作,更通过智能的 SQL 生成机制,让开发者在享受 ORM 便利的同时,不牺牲数据库原生 JSON 的性能优势。无论是用户配置、动态表单、多语言内容,还是 IoT 设备上报的异构数据,FreeSql 都能提供一套简洁、高效、跨平台的解决方案。
在“关系模型为主,JSON 为辅”的混合架构趋势下,掌握 FreeSql 的 JSON 能力,无疑将为你的 .NET 应用增添一份灵活与强大。