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. 【Hibernate】Hibernate系列2之Session详解

    Session详解 2.1.概述-一级缓存 2.2.操作session缓存方法 2.3.数据库隔离级别 2.4.持久化状态 2.5.状态转换 2.6.存储过程与触发器

  2. 22.整数二进制表示中1的个数[Get1BitCount]

    [题目] 输入一个整数,求该整数的二进制表达中有多少个1.例如输入10,由于其二进制表示为1010,有两个1,因此输出2. [分析] 如果一个整数不为0,那么这个整数至少有一位是1.如果我们把这个整数 ...

  3. Light OJ 1296 - Again Stone Game (博弈sg函数递推)

    F - Again Stone Game Time Limit:2000MS     Memory Limit:32768KB     64bit IO Format:%lld & %llu ...

  4. 信与信封问题(codevs 1222)

    题目描述 Description John先生晚上写了n封信,并相应地写了n个信封将信装好,准备寄出.但是,第二天John的儿子Small John将这n封信都拿出了信封.不幸的是,Small Joh ...

  5. Linux设置IP

    进入 vi /etc/sysconfig/network-scripts/ifcfg-eth0  root # ifconfig eth0 192.168.22.232 root # route ad ...

  6. 源码安装mysql-5.6.32.tar.gz

    http://mirrors.sohu.com/mysql/MySQL-5.6/mysql-5.6.32.tar.gz 安装依赖包: #yum install wget bison gcc gcc-c ...

  7. IntelliJ IDEA快捷键和常用设置

    一   快捷键的使用       1.文本编辑删除 ctr + y复制 ctr + D2.智能提示提示 ctr + space智能提示 ctr + shift + space完成当前语句 ctr + ...

  8. Java 对时间和日期的相关处理

    1. 获取当前系统时间和日期并格式化输出 import java.util.Date; import java.text.SimpleDateFormat; public class NowStrin ...

  9. Mysql or Mongodb LBS快速实现方案

    http://www.wubiao.info/470 前两篇文章: 查找附近的xxx 球面距离以及Geohash方案探讨 (http://www.wubiao.info/372) 微信.陌陌 架构方案 ...

  10. 电赛菜鸟营培训(四)——STM32F103CB之ADC转换

    一.ADC概念 实现模拟信号转换成数字信号就是这样子= = 二.代码框架 #include "stm32f10x.h" void delay(u32 kk) { while(kk- ...