預設情況下,NHibernate 將 enum 對應為整數、bool 對應為資料庫原生型別(SQL Server 為 bit、Oracle 為 NUMBER(1)、MySQL 為 TINYINT)。跨資料庫部署時,型別不一致會導致映射失敗或資料截斷。本文提供三種標準化持久化策略,確保 Domain Model 與資料庫結構解耦。
場景**:資料庫欄位為 VARCHAR,希望儲存「1」、「2」而非「Yes」、「No」,便於 SQL 直讀與報表工具相容。
public enum Status { Yes = 1, No = 2 }
public class Product
{
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
public virtual Status Status { get; set; }
// ...其他屬性
}
<property name="Status">
<column name="Status" sql-type="varchar(10)" default="1" not-null="true" />
</property>
原理**:NHibernate 預設 EnumType 會呼叫 Enum.ToString() 序列化為名稱;指定 sql-type="varchar" 後,ADO.NET 參數傳遞時自動轉為字串「1」/「2」,資料庫存儲字串,讀取時再轉回 Enum。
適用**:Oracle CHAR(1)、PostgreSQL CHAR(1)、通用字元欄位。
<property name="IsDelete" type="NHibernate.Type.YesNoType, NHibernate">
<column name="IsDelete" sql-type="char(1)" default="N" not-null="true" />
</property>
資料庫實際儲存:'Y' = true,'N' = false。
場景**:資料庫規範要求布林欄位統一用 CHAR(1) 存 '1'/'0',或需相容舊系統。
using System;
using System.Data;
using NHibernate;
using NHibernate.SqlTypes;
using NHibernate.UserTypes;
public class YesNo10 : IUserType
{
public bool IsMutable => false;
public Type ReturnedType => typeof(bool);
public SqlType[] SqlTypes => new[] { NHibernateUtil.String.SqlType };
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
var obj = NHibernateUtil.String.NullSafeGet(rs, names[0]);
if (obj == null) return null;
var val = (string)obj;
if (val != "1" && val != "0")
throw new Exception($"Expected '1' or '0' but was '{val}'.");
return val == "1";
}
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
var param = (IDataParameter)cmd.Parameters[index];
if (value == null) param.Value = DBNull.Value;
else param.Value = (bool)value ? "1" : "0";
}
public object DeepCopy(object value) => value;
public object Replace(object original, object target, object owner) => original;
public object Assemble(object cached, object owner) => cached;
public object Disassemble(object value) => value;
public new bool Equals(object x, object y)
{
if (ReferenceEquals(x, y)) return true;
if (x == null || y == null) return false;
return x.Equals(y);
}
public int GetHashCode(object x) => x == null ? typeof(bool).GetHashCode() + 473 : x.GetHashCode();
}
<property name="IsDelete" type="Com.Andyshi.NH3.Domain.YesNo10, Com.Andyshi.NH3">
<column name="IsDelete" sql-type="char(1)" default="0" not-null="true" />
</property>
| 目標資料庫/規範 | Enum 策略 | Boolean 策略 |
|---|---|---|
| SQL Server (bit) | 預設 int | 預設 bit |
| Oracle (CHAR(1)) | sql-type="varchar(10)" | YesNoType ('Y'/'N') 或 YesNo10 ('1'/'0') |
| MySQL (TINYINT) | 預設 int | 預設 tinyint(1) |
| 跨資料庫統一規範 | 統一 varchar(10) 存數字字串 | 統一 char(1) 存 '1'/'0' (YesNo10) |
NHibernate.Types 專案,所有 IUserType 實作置於此,映射檔統一引用。YesNo10 至所有 bool 屬性。ALTER TABLE ... ALTER COLUMN 腳本,避免型別不匹配導致部署失敗。本文出自「突破中的IT架構師」博客,請保留出處:https://virusswb.blog.51cto.com/115214/1229651