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. MVC(Model(模型) View(视图) Controller(控制器))

    复习 1.      商品表 增删改查 index.php  add.php   view.php   edit.php   action.php 2.      MVC(Model(模型)  Vie ...

  2. ios7技巧:你需要掌握的19个iOS7使用技巧

    从右往左滑动屏幕,可看到信息收到的时间. 指南针应用还可以用作水平仪,滑动屏幕即可. 被苹果称作Spotlight的搜索功能有所改变.在屏幕中间向下滑动即可打开该项功能,你可以搜索文本.邮件.应用.歌 ...

  3. Java for LeetCode 041 First Missing Positive

    Given an unsorted integer array, find the first missing positive integer. For example, Given [1,2,0] ...

  4. DP:Wooden Sticks(POJ 1065)

    摆木棍 题目大意:即使有一堆木棍,给一个特殊机器加工,木棍都有两个属性,一个是l一个是w,当机器启动的时候(加工第一根木棒的时候),需要一分钟,在这以后,设机器加工的上一根木棒的长度是l,质量是w,下 ...

  5. codeforces B.Fence 解题报告

    题目链接:http://codeforces.com/problemset/problem/363/B 题目意思:给定整数n和k,需要从n个数中找出连续的k个数之和最小,输出这连续的k个数中的第一个数 ...

  6. 案例(用封装的ajax函数检查用户名)

    本程序主要功能是检查用户输入的用户名是否在检查的范围之内 CheckUserName.htm代码: <head> <title></title> <scrip ...

  7. Andoird自定义ViewGroup实现竖向引导界面

    一般进入APP都有欢迎界面,基本都是水平滚动的,今天和大家分享一个垂直滚动的例子. 先来看看效果把: 首先是布局文件: <com.example.verticallinearlayout.Ver ...

  8. RGB888->RGB565->RGB888

     转自CB的博客:http://blog.chinaaet.com/detail/28298 在我们的计算机中,图像是以RGB888显示的,24位图每个像素保存了32bit的数据,即RGB888+Al ...

  9. -- Warning: Skipping the data of table mysql.event. Specify the --events option explicitly.

    [root@DB ~]# mysqldump -uroot -p123 --flush-logs --all-databases >fullbackup_sunday_11_PM.sql -- ...

  10. svn 提交冲突(目录下删除文件)

    [root@v01 webtest]# svn ci -m "delete kkk" svn: Commit failed (details follow): svn: Abort ...