生态产品多个设计器现已发布候选版本

查看生态总览
跳转到内容

数据库配置 ​

连接怎么配、支持哪些库、读写分离怎么开、启动时的建库建表播种、分表怎么查,以及为什么加字段后部署会炸。

配置节 XiHan:Data:SqlSugarCore。

支持的数据库 ​

ORM 是 SqlSugar,DbType 取它的枚举值。「能连上、能首次建表」与「能当独立库租户、有升级脚本」是两回事,下表分开列:

数据库DbType独立库租户可选升级脚本说明
PostgreSQLPostgreSQL✅✅默认与推荐,唯一提供 UpdateScripts 的方言
MySQL / MariaDBMySql✅—MariaDB 协议兼容,同用 MySql
SQL ServerSqlServer✅——
SQLiteSqlite✅—仓库测试用它跑建表与仓储用例
OracleOracle✅—支持但不建议
达梦 / 人大金仓Dm / Kdbndp——只在 ORM 层可用:驱动随 SqlSugarCore 一并安装,可作主库 DbType;仓库未针对它们验证
  • 独立库租户的数据库类型是 TenantDatabaseType,只有 SqlServer / MySql / PostgreSql / SQLite / Oracle 五个值,选不到达梦与人大金仓。
  • 升级脚本只提供 PostgreSQL 方言。主库或独立库用其它数据库时,全新库由 CodeFirst 建表并登记为最新版本,之后版本的结构变化要自行准备对应方言的脚本,否则开着自动升级时 PostgreSQL 脚本会在该库上执行失败并中断启动(见升级与迁移)。

本地用 Docker 起库见 开发环境。

连接配置 ​

json
{
  "XiHan": {
    "Data": {
      "SqlSugarCore": {
        "ConnectionConfigs": [
          {
            "ConfigId": "Default",
            "ConnectionString": "Server=127.0.0.1;Database=XiHanBasicApp;Username=postgres;Password=***;TrustServerCertificate=true;",
            "DbType": "PostgreSQL",
            "IsAutoCloseConnection": true,
            "SlaveConnectionConfigs": []
          }
        ]
      }
    }
  }
}
键说明
ConfigId连接唯一标识(多库 / 多租户路由用),字符串
ConnectionString主库连接串
DbType见上表
IsAutoCloseConnection自动关闭连接
SlaveConnectionConfigs[]从库;空数组 = 单库不分离

生产密码走环境变量

XiHan__Data__SqlSugarCore__ConnectionConfigs__0__ConnectionString。生产 appsettings 通常被 gitignore,需在服务器单独维护。

读写分离 ​

填了从库后 SELECT 自动走从库、写与事务走主库,业务代码无感知:

json
"SlaveConnectionConfigs": [
  { "ConnectionString": "Server=从库1;Database=XiHanBasicApp;Username=postgres;Password=***;" },
  { "ConnectionString": "Server=从库2;Database=XiHanBasicApp;Username=postgres;Password=***;" }
]

HitRate 配不上,别写

HitRate(读权重)是 SqlSugar 的字段、绑不上 appsettings——写了也无效、恒为 0。

框架会把权重为 0 的从库归一化为 DefaultSlaveHitRate(默认 10),所以不写 HitRate 也能等权分担读。

需要差异化权重、挂 ConfigureExternalServices 或自写探活,用代码钩子 XiHanSqlSugarCoreOptions.ConfigureConnectionConfigs。

从库健康探针 ​

键默认说明
EnableSlaveHealthCheckfalse周期探活,不可用从库自动摘除读权重
SlaveHealthCheckIntervalSeconds30探测周期
SlaveFailureCooldownSeconds120故障冷却窗口,恢复后先冷却再回填权重避免抖动
DefaultSlaveHitRate10权重归一化默认值

启动初始化 ​

键说明
EnableDbInitialization启动时自动建库(库不存在则创建);同时是整个初始化流程的总开关,关掉则建表与播种一并跳过
EnableTableInitialization启动时 CodeFirst 建表
EnableDataSeeding启动时写入种子数据
TableInitialization建哪些表(不填=全部)
DataSeeding跑哪些种子(不填=全部)

三个开关都开时,首次启动一条龙:建库 → 建表 → 播种。这也是「拿到代码配好连接串就能跑」的原因。

挑表挑种子 ​

开关只管开不开,范围由 TableInitialization / DataSeeding 决定,默认全量。三种用法:

jsonc
"TableInitialization": {
  // All(默认):扫到的实体都建;OptIn:只建显式标了 [TableInitialization] 的实体
  "Mode": "All",
  // 支持 * ? 通配,按实体类名/实体全名/表名任一匹配
  "ExcludedTables": [ "sys_diff_log" ]
},
"DataSeeding": {
  // 按种子名称 / 类名 / 全名排除;演示数据的开关是 Saas:Seed:EnableDemoData,不靠这里
  "ExcludedSeeders": [ "[Chat]内建定时任务" ]
}

实体侧还能直接标注:[TableInitialization(false)] 表示这张表由自己维护(框架不建)、 [TableInitialization(Target = DbInitializationTarget.Platform)] 表示只建在平台库、独立库租户不建。 本应用的平台目录、账号与授权、读共享模板等实体标的是 [PlatformDataSource]:不只建表只在平台库,运行期读写也固定走平台库,见 多租户:库隔离。 要整套自己实现就 Replace 掉 IDbEntityTypeProvider / IDataSeederSelector, 细节见 Framework 数据访问。

建表只建不改 ​

加了字段部署后必报「列不存在」

DbInitializer 表存在就跳过创建(日志里是「表已存在,跳过创建」),它从不为已有表补列。

给既有实体加字段后部署,运行到该表的查询就会报 42703 column does not exist(PG)或等价错误。

三条路:

  • 正式版本在 WebHost/UpdateScripts/<版本>/<版本>.sql 编写前向迁移(推荐),启动时由升级引擎执行;
  • 可丢弃数据的本地环境重建数据库;
  • 紧急修复时手工 ALTER TABLE,随后仍要补入版本脚本。

CodeFirst 负责首次建表;已有库的结构和数据变化由 Framework Upgrade 引擎执行。

升级状态不是本地文件

升级引擎用 SysVersion 和 SysMigrationHistory 记录每个数据库的版本与脚本结果,不使用 version.txt;独立数据库租户逐库升级。XiHan:Upgrade:EnableAutoCheckOnStartup 为 true(缺省即为 true)时启动会自动执行待执行脚本,失败即中断启动,详见升级与迁移。

种子数据 ​

分两类:

类别开关内容
基础数据始终播种超级管理员、操作字典、各模块的权限目录与菜单、套餐、参数、默认存储、消息模板、内建 OAuth 应用、内建定时任务、代码生成内置模板——系统运行所需,不建任何租户
演示数据Saas:Seed:EnableDemoData覆盖各种情况的演示租户、组织、角色、账号,以及演示通知与字典

EnableDemoData 为 true 才写演示数据,缺省或 false 都不写,写错(不是布尔值)直接启动失败。各环境在自己的配置文件里写明:仓库的开发环境配置开启,生产环境在部署处的 appsettings.Production.json(不入库)里设置。

初始超级管理员 superadmin / SuperAdmin@123,密码由种子写入并标记为需要本人改密,生产首次登录后立即修改,并建议在参数「密码设置」里开启强制改密。

执行阶段、写入口径(哪些每次对齐、哪些只写一次)、演示租户与账号清单见 框架简介:种子数据。

分表查询 ​

按月分表的实体(日志类)物理表名形如 Sys_Access_Log_20260801。

分表不能当普通表查

读写都必须显式挂 SqlSugar 的分表 API,否则表名里的 {year}{month}{day} 占位符不会被替换:

  • 查询、插入:.SplitTable();
  • 条件删除:.SplitTable(tabs => tabs)——无参重载只支持按实体集合删除,条件删除会在运行时抛异常。

SQL 日志与诊断 ​

键默认说明
EnableSqlLogfalse打印所有 SQL。生产建议关闭,日志会爆量
EnableSqlErrorLogtrue记录 SQL 异常
EnableSlowSqlLogtrue记录慢 SQL
SlowSqlThresholdMilliseconds10000慢 SQL 阈值(毫秒),纯观测用途、不影响语句执行
CommandTimeoutSeconds300ADO 命令超时;0/负值不覆盖;须明显大于慢 SQL 阈值

排查性能问题的顺序:先看慢 SQL 日志找出目标语句 → 临时开 EnableSqlLog 看完整 SQL 与参数 → 定位后关掉。

数据变更日志 ​

键默认说明
EnableDiffLogfalse实体差异日志(SysDiffLog)总开关

数据变更日志页恒空的头号原因

默认 false——不开则 Diff AOP 根本不挂载,收集到的差异被直接丢弃。生产 appsettings 常被 gitignore,最容易漏配的就是这一项。

代价:开启后 update/delete 会先查一次旧值算差异,每个写操作多一次 SELECT。

覆盖范围:只覆盖走仓储的写。绕过仓储直接用 DbClient 的写(如 UpdateColumns)不产生差异日志。

一个仓储层的强制约定 ​

仓储里禁止显式调用 .EnableQueryFilter()

框架默认 EnableAutoUpdateQueryFilter / EnableAutoDeleteQueryFilter 均为 true,SqlSugar 的 Updateable<T>() / Deleteable<T>() 工厂内部已经自动挂了一次租户/软删过滤。

再显式挂一次 → 同一份过滤烘进 WHERE 两遍 → 生成同名参数 @constant1001 → 一旦叠加 Diff 的 GetDiffTable 重查旧值就崩(MySQL 驱动直接抛,PG 容忍重名故不崩,但仍是冗余死条件)。

.EnableDiffLogEvent() 保留,它单独用是安全的。

排查 ​

现象原因
启动连不上库连接串、防火墙、库是否已启动
部署后报「列不存在」版本升级脚本缺少对应 ALTER TABLE 或未成功执行,见上
数据变更日志空EnableDiffLog 没开
写操作报参数重名仓储里显式调了 .EnableQueryFilter()
从库没分担读确认 SlaveConnectionConfigs 填了;HitRate 写了也无效
日志查不到数据分表查询没挂 .SplitTable()
多节点主键冲突WorkerId 没逐节点改,见 实体基类

相关页面 ​

Released under The MIT License