http://www.zvolkov.com/clog/2009/07/09/why-nhibernate-updates-db-on-commit-of-read-only-transaction/

Always be careful with NULLable fields whenever you deal with NHibernate. If your field is NULLable in DB, make sure corresponding .NET class uses Nullable type too. Otherwise, all kinds of weird things will happen. The symptom is usually will be that NHibernate will try to update the record in DB, even though you have not changed any fields since you read the entity from the database.

The following sequence explains why this happens:

  1. NHibernate retrieves raw entity's data from DB using ADO.NET
  2. NHibernate constructs the entity and sets its properties
  3. If DB field contained NULL the property will be set to the defaul value for its type:
    • properties of reference types will be set to null
    • properties of integer and floating point types will be set to 0
    • properties of boolean type will be set to false
    • properties of DateTime type will be set to DateTime.MinValue
    • etc.
  4. Now, when transaction is committed, NHibernate compares the value of
    the property to the original field value it read form DB, and since the
    field contained NULL but the property contains a non-null value,
    NHibernate considers the property dirty, and forces an update of the
    enity.

Not only this hurts performance (you get
extra round-trip to DB and extra update every time you retrieve the
entity) but it also may cause hard to troubleshoot errors with DateTime
columns. Indeed, when DateTime property is initialized to its default
value it's set to 1/1/0001. When this value is saved to DB, ADO.NET's
SqlClient can't convert it to a valid SqlDateTime value since the
smallest possible SqlDateTime is 1/1/1753!!! The exception it throws
looks like this:

NHibernate.Event.Default.AbstractFlushingEventListener - Could not synchronize database state with session 
NHibernate.HibernateException:
An exception occurred when executing batch queries --->
System.Data.SqlTypes.SqlTypeException: SqlDateTime overflow. Must be
between 1/1/1753 12:00:00 AM and 12/31/9999 11:59:59 PM. 
at System.Data.SqlTypes.SqlDateTime.FromTimeSpan(TimeSpan value) 
   at System.Data.SqlTypes.SqlDateTime.FromDateTime(DateTime value)
   at System.Data.SqlClient.MetaType.FromDateTime(DateTime dateTime, Byte cb)
   at
System.Data.SqlClient.TdsParser.WriteValue(Object value, MetaType type,
Byte scale, Int32 actualLength, Int32 encodingByteSize, Int32 offset,
TdsParserStateObject stateObj)
   at
System.Data.SqlClient.TdsParser.TdsExecuteRPC(_SqlRPC[] rpcArray, Int32
timeout, Boolean inSchema, SqlNotificationRequest notificationRequest,
TdsParserStateObject stateObj, Boolean isCommandProc)
   at
System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior
cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean
async)
   at
System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior
cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String
method, DbAsyncResult result)
   at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(DbAsyncResult result, String methodName, Boolean sendToPipe)
   at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
   at System.Data.SqlClient.SqlCommandSet.ExecuteNonQuery()
   at NHibernate.AdoNet.SqlClientSqlCommandSet.ExecuteNonQuery()
   --- End of inner exception stack trace ---
   at NHibernate.AdoNet.SqlClientSqlCommandSet.ExecuteNonQuery()
   at NHibernate.AdoNet.SqlClientBatchingBatcher.DoExecuteBatch(IDbCommand ps)
   at NHibernate.AdoNet.AbstractBatcher.ExecuteBatch()
   at NHibernate.Engine.ActionQueue.ExecuteActions(IList list)
   at NHibernate.Engine.ActionQueue.ExecuteActions()
   at NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions(IEventSource session)
   at NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent event)
   at NHibernate.Impl.SessionImpl.Flush()
   at NHibernate.Transaction.AdoTransaction.Commit()

The easiest fix is to make the class
property use Nullable<T> type, in this case "DateTime?".
Alternatively, you could implement a custom type mapper by implementing
IUserType with its Equals method properly comparing DbNull.Value with
whatever default value of your value type. In our case Equals would need
to return true when comparing 1/1/0001 with DbNull.Value. Implementing a
full-functional IUserType is not really that hard but it does require
knowledge of NHibernate trivia so prepare to do some substantial
googling if you choose to go that way.

Hope this helps somebody!

Why NHibernate updates DB on commit of read-only transaction的更多相关文章

  1. ABAP-关于隐式与显式的DB Commit

    转载:https://www.cnblogs.com/liaojunbo/archive/2011/07/11/2103491.html 1.显式的DB Commit 显式的DB Commit并没有对 ...

  2. The Architecture of Open Source Applications: Berkeley DB

    最近研究内存关系数据库的设计与实现,下面一篇为berkeley db原始两位作为的Berkeley DB设计回忆录: Conway's Law states that a design reflect ...

  3. spring.net 集成nhibernate配置文件(这里暴露了GetCurrentSession 对于 CurrentSession unbond thread这里给出了解决方法)

    我这里主要分成了两个xml来进行spring.net管理实际情况中可自己根据需要进行分类 Dao2.xml <?xml version="1.0" encoding=&quo ...

  4. NHibernate系列文章十四:NHibernate事务

    摘要 NHibernate实现事务机制非常简单,调用ISession.BeginTransaction()开启一个事务对象ITransaction,使用ITransaction.Commit()提交事 ...

  5. NHibernate系列文章十六:使用程序集管理NHibernate项目(附程序下载)

    摘要 在实际的项目中,经常是将NHibernate的实体关系映射类做成独立的工程(assembly dll),只对外提供Session调用的接口.这个程序集作为数据访问层,可以被上面的多个工程(ASP ...

  6. NHibernate Session-per-request and MiniProfiler.NHibernate

    NHibernate Session-per-request and MiniProfiler.NHibernate 前言 1.本文以mvc3为例,借鉴开源项目 NerdDnner项目完成nhiber ...

  7. Python DB operation

    mysql http://www.cnblogs.com/zhangzhu/archive/2013/07/04/3172486.html 1.连接到本机上的MYSQL.首先打开DOS窗口,然后进入目 ...

  8. sqlachemy 获取新创建对象的id,flush与commit

    for account_info in valid_account_detail: try: account = account_info.get('account') password = acco ...

  9. 关于NHibernate的一些代码

    SessionManager using System; using System.IO; using System.Runtime.Serialization; using System.Runti ...

随机推荐

  1. Linux生产服务器Shell脚本分享

    Linux生产服务器Shell脚本分享 2012-6-6 86市场网 linux 作为一名Linux/unix系统管理员,我经常遇到人问这个问题:shell能做什么?PHP这么强大,为什么不用PHP来 ...

  2. error adding symbols: DSO missing from command line collect2: error: ld returned 1 exit status

    Windows服务器Azure云编译安装MariaDB教程 www.111cn.net 编辑:future 来源:转载 安装MariaDB数据库最多用于linux系统中了,下文给各位介绍在Window ...

  3. There is no getter for property named 'purchaseApplyId' in 'class java.lang.Long'

    mapper.xml: <delete id="deleteByPurchaseAppyId" parameterType="Long"> < ...

  4. process vs thread

    process vs thread http://blog.csdn.net/mishifangxiangdefeng/article/details/7588727 6.进程与线程的区别:系统调度是 ...

  5. iOS 中的字体预览

    要预览iOS的各种字体的效果,可以访问http://iosfonts.com

  6. VMware安装64位操作系统提示Intel VT-x处于禁用状态的解决办法

    用VMware安装64位操作系统时,如果目前本地的操作系统是64位的,那么可以说明CPU是肯定支持64位的,这时候如果提示此主机支持 Intel VT-x,但 Intel VT-x 处于禁用状态.这个 ...

  7. Java for LeetCode 150 Evaluate Reverse Polish Notation

    Evaluate the value of an arithmetic expression in Reverse Polish Notation. Valid operators are +, -, ...

  8. centos下安装五笔输入法的教程

    [root@ok ~]# yum update [root@ok ~]# yum install ibus-table-chinese-wubi-haifeng 以上两步已经成功!! #yum ins ...

  9. 推荐:移动端前端UI库—Frozen UI、WeUI、SUI Mobile

    Frozen UI 自述:简单易用,轻量快捷,为移动端服务的前端框架. 主页:http://frozenui.github.io/ 开发团队:QQVIP FD Team Github:https:// ...

  10. ***Linux文件夹文件创建、删除、改名

    Linux删除文件夹命令 linux删除目录很简单,很多人还是习惯用rmdir,不过一旦目录非空,就陷入深深的苦恼之中,现在使用rm -rf命令即可.直接rm就可以了,不过要加两个参数-rf 即:rm ...