跨資料庫支援 × 自動升級 × 一致性設計

在 ERP 系統中,資料表結構常隨著需求變動而頻繁調整,且往往需要同時支援 SQL Server/MySQL/PostgreSQL/Oracle 等多種資料庫。
若直接在程式中使用各資料庫的原生型別(如 NVARCHAR、NUMBER 等),一旦要異動欄位型別或調整長度,就必須修改所有 SQL 定義與程式碼,維護與遷移成本極高。
BeeNET 採用抽象資料型別(Abstract Data Type)的概念,以抽象的 FieldDbType 列舉來定義資料欄位,再由 DbTable 作為資料表結構定義物件。
部署時交由比對工具與命令產生器轉換為各資料庫對應的 DDL / DML 語法,達成跨資料庫支援與自動升級。
String、Text、DateTime 等直觀型別,由框架自動轉換。varchar/nvarchar 等差異。抽象資料型別以列舉表示,統一由 DbTable 欄位的 DbType 屬性參照:
/// <summary>
/// 欄位的抽象資料型別(跨資料庫對應)。
/// </summary>
public enum FieldDbType
{
String, // 一般字串(具長度)
Text, // 大量文字
Boolean, // 布林值
AutoIncrement, // 自動遞增整數(資料表主鍵常用)
Short, // 16-bit 整數
Integer, // 32-bit 整數
Long, // 64-bit 整數
Decimal, // 十進位數值(高精度)
Currency, // 金額(預設 19,4)
Date, // 日期
DateTime, // 日期時間
Guid, // 全域唯一識別碼
Binary // 二進位資料
}
| FieldDbType | SQL Server | MySQL | PostgreSQL | Oracle |
|---|---|---|---|---|
| String | NVARCHAR(n) | VARCHAR(n) | VARCHAR(n) | VARCHAR2(n) |
| Text | NVARCHAR(MAX) | TEXT | TEXT | CLOB |
| Boolean | BIT | TINYINT(1) / BOOLEAN | BOOLEAN | NUMBER(1) |
| AutoIncrement | INT IDENTITY(1,1) | INT AUTO_INCREMENT | SERIAL / INT GENERATED ALWAYS AS IDENTITY | NUMBER + SEQUENCE |
| Short | SMALLINT | SMALLINT | SMALLINT | NUMBER(5) |
| Integer | INT | INT | INTEGER | NUMBER(10) |
| Long | BIGINT | BIGINT | BIGINT | NUMBER(19) |
| Decimal | DECIMAL(p,s) | DECIMAL(p,s) | NUMERIC(p,s) | NUMBER(p,s) |
| Currency | DECIMAL(19,4) | DECIMAL(19,4) | NUMERIC(19,4) | NUMBER(19,4) |
| Date | DATE | DATE | DATE | DATE |
| DateTime | DATETIME | DATETIME | TIMESTAMP | TIMESTAMP |
| Guid | UNIQUEIDENTIFIER | CHAR(36) / BINARY(16) | UUID | RAW(16) |
| Binary | VARBINARY(MAX) | BLOB | BYTEA | BLOB |
FieldDbType 宣告欄位結構與限制(長度、精度、是否必填)。範例:使用 TableSchemaBuilder 建立或更新資料表
// 載入指定 tableName 的 DbTable 定義,執行資料結構比對與更新
var builder = new TableSchemaBuilder(databaseId);
builder.Execute(dbName, tableName);
此機制可於部署階段自動比對結構並同步升級。
DbTable 物件會序列化為 XML 格式,存放於專案中以供版本控制與部署使用。每個資料表的結構定義皆以 XML 檔({TableName}.DbTable.xml)保存,確保開發、測試與正式環境的結構自動保持一致。
以 st_user 資料表為例,示範 DbTable 定義方式:
<?xml version="1.0" encoding="utf-8"?>
<DbTable DbName="common" TableName="st_user" DisplayName="用戶">
<Fields>
<DbField FieldName="sys_no" Caption="流水號" DbType="AutoIncrement" />
<DbField FieldName="sys_rowid" Caption="唯一識別" DbType="Guid" />
<DbField FieldName="sys_id" Caption="用戶帳號" DbType="String" Length="20" />
<DbField FieldName="sys_name" Caption="用戶名稱" DbType="String" Length="20" />
<DbField FieldName="password" Caption="登入密碼" DbType="String" Length="40" />
<DbField FieldName="email" Caption="電子郵件" DbType="String" Length="100" />
<DbField FieldName="note" Caption="備註" DbType="String" Length="200" />
<DbField FieldName="sys_insert_time" DisplayName="寫入時間" DbType="DateTime" />
</Fields>
<Indexes>
<DbTableIndex Name="pk_{0}" Unique="true" PrimaryKey="true">
<IndexFields>
<IndexField FieldName="sys_no" />
</IndexFields>
</DbTableIndex>
<DbTableIndex Name="rx_{0}" Unique="true">
<IndexFields>
<IndexField FieldName="sys_rowid" />
</IndexFields>
</DbTableIndex>
<DbTableIndex Name="ux_{0}" Unique="true">
<IndexFields>
<IndexField FieldName="sys_id" />
</IndexFields>
</DbTableIndex>
</Indexes>
</DbTable>
抽象資料型別讓資料結構設計更具一致性與延展性,
DbTable、TableSchemaComparer 與 TableSchemaBuilder 共同構成了 BeeNET 在資料結構層的自動化核心。
透過這樣的架構,ERP 系統能在多資料庫與持續演進的需求下,
仍保持結構一致、部署順暢,並有效降低長期維護成本。
FieldDbType 的設計使開發人員能專注於商業邏輯與流程創新,
而不必被資料庫型別與結構差異所綁架,真正實現高效與穩定並行的開發模式。
📘 HackMD 原文筆記:
👉 https://hackmd.io/@jeff377/field-dbtype-design
📢 歡迎轉載,請註明出處
📬 歡迎追蹤我的技術筆記與實戰經驗分享
Facebook | HackMD | GitHub | NuGet