2012-09-11 84 views
3

我第一次使用流利的nhibernate來逐步淘汰用於訪問遠程MySql服務器的現有數據庫訪問層。如何使用nhibernate將數據庫字段設置爲服務器時間

客戶端大多是虛擬化的,可以在任何時間點(任何時間長度(幾秒到幾天))進入休眠/休眠狀態。即使它們通常與服務器時間同步,但無法保證,這意味着我無法使用爲某些字段提供DateTime的客戶端。

如何獲得nhibernate設置/更新某些字段的服務器時間(最好通過調用UTC_TIMESTAMP()函數)?

對象:

public class MyObject{ 
    public virtual UInt64 Id { get; set; } 
    public virtual String Status{ get; set; } 
    public virtual String SomeData{ get; set; } 

    //Set only once when the Object is inserted 
    public virtual DateTime TimeCreated { get; set; } 
    //Set every time the object is updated 
    public virtual DateTime TimeLastUpdate { get; set; } 
    //Set every time the 'Status' column is updated 
    public virtual DateTime TimeStatusLastChanged { get; set; } 
    //This is a user provides standard datetime field 
    public virtual DateTime SomeUserSpecifiedTime { get; set; } 
} 

映射:

Id(x => x.Id) 
    .GeneratedBy.Native(); 
Map(x => x.Status); 
Map(x => x.SomeData); 
Map(x => x.SomeUserSpecifiedTime); 
//? --> 
Map(x => x.TimeCreated) 
    .Not.Update(); 
Map(x => x.TimeLastUpdate); 
Map(x => x.TimeStatusLastChanged); 
//<-- ? 

我想這三個Time* -fields使用UTC_TIMESTAMP()功能,當它們被設置/更新,所以服務器端的日期/時間變插入。 DateTime SomeUserSpecifiedTime字段是標準映射字段。

到現在爲止,我的數據庫訪問類包含了以這種方式創建查詢的邏輯。我可以使用.Generated.Insert/Always映射創建數據庫觸發器,但我希望有一個nhibernate/code-only方法。

我發現的一個解決方案是從服務器獲取時間併爲插入/更新提供時間,但這樣做已經結束了,因爲獲取和插入/更新之間的時間可能很長。

+0

爲什麼不把時間設置爲UTC而不是服務器時間? –

+0

所有時間都在內部UTC,也在客戶端。不幸的是客戶時間可能不同,所以唯一的權威時間是服務器時間。雖然我使用ntp時間服務器來保持時間同步,但這並不總是足夠的。 – Darcara

+0

只是一個想法,你可以計算客戶端與服務器時間偏移量,當應用程序進入時,並使用它來設置服務器時間邏輯。可能不是最傻瓜的解決方案。 –

回答

1

我決定來解決使用觸發器的問題,這是不是最佳的,但工程現在。不幸的是NHibernate的不具有觸發器的概念,所以你必須爲輔助數據庫對象,這會導致一些問題

  • SchemaValidator和SchemaUpdate工具不會拿起auxDbObjects,因此不會更新申報他們/創建他們。 SchemaExport會創建它們,但會這樣做兩次,導致更多的代碼重複。
  • 需要爲每個觸發器創建並註冊一個實例,這很麻煩。

它可能是這個樣子

public class MyTrigger : IAuxiliaryDatabaseObject{ 

    public string SqlCreateString(Dialect dialect, IMapping p, string defaultCatalog, string defaultSchema){ 
    //The drop is important, because the shema export calls and executes this twice. 
    return @" 
DROP TRIGGER IF EXISTS myTrigger; 

CREATE TRIGGER myTrigger BEFORE INSERT ON myObjectTable FOR EACH ROW 
BEGIN 
set new.TimeAdded = UTC_TIMESTAMP(); 
END"; 
    } 

    public string SqlDropString(Dialect dialect, string defaultCatalog, string defaultSchema){ 
    return @"DROP TRIGGER IF EXISTS myTrigger"; 
    } 

    public void AddDialectScope(string dialectName){ 
    throw new NotImplementedException(); 
    } 

    public bool AppliesToDialect(Dialect dialect){ 
    return true; 
    } 

    public void SetParameterValues(IDictionary<string, string> parameters){ 
    throw new NotImplementedException(); 
    } 
    } 

配置中的每個觸發器/ AUX。數據庫對象必須單獨添加:

Fluently.Configure() 
    .Database(...) 
    .ExposeConfiguration(conf => 
    { 
    conf.AddAuxiliaryDatabaseObject(new MyTrigger()); 
    }); 

映射

Map(x => x.TimeCreated) 
    .Not.Update() 
    .CustomType("UtcDateTime") 
    .ReadOnly() 
    .Generated.Insert() //.Generated.Always() for columns that track lastModification 
    ; 

自定義用戶類型或攔截所以看來這是唯一可能的方式在瞬間進行實現這個不遺憾的是工作。

0

這應該做的伎倆

Map(x => x.TimeStatusLastChanged) 
      .Not.Nullable() 
      .Default("UTC_TIMESTAMP()"); 
+1

UTC_TIMESTAMP()函數對於DateTime或Timestamp列不是有效的默認值。此外,它只適用於插入,而不是更新。 – Darcara

相關問題