在使用 ActiveRecord 自動建立資料表時,許多開發者認為「表已存在則不會有任何動作」。但實際測試發現:若直接呼叫 ActiveRecordStarter.CreateSchema(),已存在的資料表會被 Drop 重建,導致資料全部遺失。本文深入原始碼分析這一行為差異,釐清各 Schema 方法的真實語意。
| 方法 | 用途 | 底層實現 | 資料安全性 |
|---|---|---|---|
CreateSchema() | 依實體類別建立資料表 | NHibernate SchemaExport.Create(false, true) | ⚠️ 會刪除現有表並重建 |
DropSchema() | 刪除資料表 | NHibernate SchemaExport.Create(true, false) | ❌ 僅刪除表結構 |
CreateSchemaFromFile() | 執行外部 SQL 腳本建表 | ActiveRecord 自有 ARSchemaCreator | ✅ 表存在則忽略錯誤,資料安全 |
GenerateCreationScripts() | 產生建表 SQL 腳本檔 | NHibernate SchemaExport | ✅ 僅產生檔案,不執行 |
GenerateDropScripts() | 產生刪表 SQL 腳本檔 | NHibernate SchemaExport | ✅ 僅產生檔案,不執行 |
追蹤原始碼:
// ActiveRecordStarter.CreateSchema()
public static void CreateSchema() {
foreach (var config in ActiveRecordBase._holder.GetAllConfigurations()) {
var export = CreateSchemaExport(config);
export.Create(false, true); // script=false, export=true
}
}
// NHibernate SchemaExport.Create()
public void Create(bool script, bool export) {
Execute(script, export, false, true); // justDrop=false, format=true
}
// NHibernate SchemaExport.Execute() 關鍵邏輯
public void Execute(bool script, bool export, bool justDrop, bool format) {
// 1. 總是先執行 DROP 腳本
// 2. 若 justDrop=false,再執行 CREATE 腳本
// 3. 相當於執行:
// IF EXISTS (SELECT * FROM sysobjects WHERE ...) DROP TABLE Blogs
// CREATE TABLE Blogs (...)
}
結論:CreateSchema() 內部會無條件先執行 DROP TABLE IF EXISTS,再 CREATE TABLE。這在開發環境重建結構方便,但在正式環境極度危險!
此方法透過 ARSchemaCreator.ExecuteScriptParts() 逐條執行 SQL,catch 區塊直接吞掉例外並輸出 Debug 訊息:
try {
statement.CommandText = part;
statement.ExecuteNonQuery();
} catch (Exception ex) {
Debug.WriteLine($"SQL: {part} threw {ex.Message}. Ignoring");
}
因此:若資料表已存在,執行 CREATE TABLE 會拋出例外,被忽略,現有表結構與資料完好無損。這才是「表不存在才建立」的正確語意。
CreateSchema() 快速重建結構,配合測試資料重灌。GenerateCreationScripts()),由 DBA 審核後手動執行。CreateSchemaFromFile() 執行版本控管的遷移腳本(需自行處理 IF NOT EXISTS 邏輯)。ActiveRecord 的 Schema 方法語意各異,CreateSchema() 實為「Drop & Recreate」,CreateSchemaFromFile() 才是「安全建表」。生產環境務必採用腳本管控方式,避免不可逆的資料災難。