NHibernate 实体类中重写 Equals() 和 GetHashCode()


        在正常的对象操作中,当两个对象都是通过 new 或者其它方式创建出来,尽管它们在属性和行为上是一致的,但我们还是得承认他们是属于不同的两个事物,就像现实世界中的单卵双生的双胞胎虽然各方面都很像,但他们到底还是属于不同的个体。但当这种情况发生在 NHibernate(以下简称NH), 实休类中时,我们就不能这么草率地下结论了,通常实体类中每个实例映射着数据库表中的一行记录,我们知道一个良好设计的数据库表字段中一个唯一标识是必不可少的,那就是主键,主键相当于每个公民的身份证号,必须是唯一且无重复的,不管运行多少次 select * from table where id = @id ,只要 @id 值不改变我们就认为查询出来的是同一行记录,同样的道理映射到实体类中也是主键的属性值相等我们就应该承认它们是相等的,一张身份证它可以有多个复印件,但不管怎样它都是属于同一个人的。

        由于 System.Object 中是通过 Equals() 方法来确定两个对象是否相等,因为 Equals() 方法中两个对象具有相同的“值”,那么即使它们不是同一实例,这样的 Equals 实现仍返回 true。两个相同的对象它的 GetHashCode() 结果也必须是相等的,在 MSDN 上也明确指出,“重写 Equals 的类型也必须重写 GetHashCode;否则,Hashtable 可能不正常工作。”“重写 GetHashCode 的派生类还必须重写 Equals,以保证被视为相等的两个对象具有相同的哈希代码;否则,Hashtable 可能不会正常工作。” 

   下面是引用 Hibernate参考文档 第4章 中对 实现equals()和hashCode() 的解释: 

4.3. 实现equals()hashCode()如果你需要混合使用持久化类(比如,在一个Set中),你必须重载equals() 和 hashCode()方法。

这仅适用于那些在两个不同的Session中装载的对象,Hibernate在单个Session中仅保证JVM 辨别( a == b ,equals()的默认实现)!

就算两个对象a和b实际是同一行数据库内容(它们拥有同样的主键值作为辨识符),我们也不能保证在特定的Session 之外它们是同一个Java实例。

最显而易见的实现equals()/hashCode()方法的办法就是比较两个对象的标识值。如果这个值是同堂的,他们必定是直线同一条数据库行,所以它们是相等的(如果都被加入到Set,在Set中只应该出现一个元素)。不幸的是,我们不能使用这种办法。Hibernate只会对已经持久化的对象赋予标识值,新创建的实例将不会有任何标识符值!我们推荐使用商业关键字相等原则来实现equals()和hashCode()。

商业关键字相等意味着equals()方法只比较那些组成商业关键字的属性,它对应着真实世界中的实例(自然的候选关键字)


下面是实现方法(该方法出自 http://cs.nerdbank.net/blogs/jmpinline/archive/2006/01/24/126.aspx ):

ExpandedBlockStart.gif ContractedBlock.gif /**/ /// <summary>
InBlock.gif
/// Tests whether this and another object are equal in a way that
InBlock.gif
/// will still pass when proxy objects are being used.
ExpandedBlockEnd.gif
/// </summary>

None.gif public   override   bool  Equals( object  obj)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    ClassNameHere other 
= obj as ClassNameHere;
InBlock.gif    
if (other == nullreturn false;
InBlock.gif    
if (Id == 0 && other.Id == 0)
InBlock.gif        
return (object)this == other;
InBlock.gif    
else
InBlock.gif        
return Id == other.Id;
ExpandedBlockEnd.gif}

None.gif
None.gif
public   override   int  GetHashCode()
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    
if (Id == 0return base.GetHashCode();
InBlock.gif    
string stringRepresentation = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName + "#" + Id.ToString(); return stringRepresentation.GetHashCode();
ExpandedBlockEnd.gif}

伴随着 .NET 2.0 的发布,加入了泛型代码中 default 关键字,我们可以把 snippet  做得更加通用而不仅限于 Int 数据类型,

None.gif <? xml version="1.0" encoding="utf-8"  ?>
None.gif
< CodeSnippets  xmlns ="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet" >
None.gif  
< CodeSnippet  Format ="1.0.0" >
None.gif    
< Header >
None.gif      
< Title > Entity Equals and GetHashCode </ Title >
None.gif      
< Shortcut > nheq </ Shortcut >
None.gif      
< Author > yyw </ Author >
None.gif      
< HelpUrl >
None.gif        http://www.cnblogs.com/yyw84/archive/2006/09/17/506695.html
None.gif        http://cs.nerdbank.net/blogs/jmpinline/archive/2006/01/24/126.aspx
None.gif      
</ HelpUrl >
None.gif      
< Description ></ Description >
None.gif      
< SnippetTypes >
None.gif        
< SnippetType > Expansion </ SnippetType >
None.gif      
</ SnippetTypes >
None.gif      
< Description > Inserts the Equals and GetHashCode methods that NHibernate requires to run correctly. </ Description >
None.gif    
</ Header >
None.gif    
< Snippet >
None.gif      
< Declarations >
None.gif        
< Literal  Editable ="true" >
None.gif          
< ID > type </ ID >
None.gif          
< ToolTip > The name of the class being injected. </ ToolTip >
None.gif          
< Default > ClassName </ Default >
None.gif        
</ Literal >
None.gif        
< Literal >
None.gif          
< ID > id </ ID >
None.gif          
< ToolTip > 主键ID </ ToolTip >
None.gif          
< Default > Id </ Default >
None.gif        
</ Literal >
None.gif        
< Literal >
None.gif          
< ID > idtype </ ID >
None.gif          
< ToolTip > 主键属性类型 </ ToolTip >
None.gif          
< Default > int </ Default >
None.gif        
</ Literal >
None.gif      
</ Declarations >
None.gif      
< Code  Language ="CSharp" >
None.gif        
<![CDATA[ #region Equals and GetHashCode
None.gif        
None.gif        /// <summary>
None.gif        /// Tests whether this and another object are equal in a way that
None.gif        /// will still pass when proxy objects are being used.
None.gif        /// </summary>
None.gif        public override bool Equals(object obj)
None.gif        {
None.gif          $type$ other = obj as $type$;
None.gif          
None.gif          if (other == null)
None.gif            return false;
None.gif            
None.gif          if ($id$== default($idtype$) && other.$id$ == default($idtype$))
None.gif            return (object)this == other;
None.gif          else
None.gif            return $id$ == other.$id$;
None.gif        }
None.gif        
None.gif        public override int GetHashCode()        
None.gif        {    
None.gif          if ($id$ == default($idtype$)) 
None.gif            return base.GetHashCode();
None.gif            
None.gif          string stringRepresentation = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName + "#" + $id$.ToString();
None.gif          return stringRepresentation.GetHashCode();
None.gif        }
None.gif        
None.gif        #endregion
None.gif        $end$
]]>
None.gif      
</ Code >
None.gif    
</ Snippet >
None.gif  
</ CodeSnippet >
None.gif
</ CodeSnippets >

为了做到一劳永逸,接下来要做的事就是把它添加到我的 CodeSmith 模板中了,添加的内容如下:

ContractedBlock.gif ExpandedBlockStart.gif Equals and GetHashCode #region Equals and GetHashCode
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif
/**//// <summary>
InBlock.gif
/// Tests whether this and another object are equal in a way that
InBlock.gif
/// will still pass when proxy objects are being used.
ExpandedSubBlockEnd.gif
/// </summary>

InBlock.gifpublic override bool Equals(object obj)
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gif    
<%= ClassName(SourceTable) %> other = obj as <%= ClassName(SourceTable) %>;
InBlock.gif
InBlock.gif    
if (other == null)
InBlock.gif        
return false;
InBlock.gif
InBlock.gif    
if (<%= IdName(SourceTable) %> == default(<%= IdMemberType(SourceTable) %>&& other.<%= IdName(SourceTable) %> == default(<%= IdMemberType(SourceTable) %>))
InBlock.gif        
return (object)this == other;
InBlock.gif    
else
InBlock.gif        
return <%= IdName(SourceTable) %> == other.<%= IdName(SourceTable) %>;
ExpandedSubBlockEnd.gif}

InBlock.gif
InBlock.gif
public override int GetHashCode()
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gif    
if (<%= IdName(SourceTable) %> == default(<%= IdMemberType(SourceTable) %>))
InBlock.gif        
return base.GetHashCode();
InBlock.gif
InBlock.gif    
string stringRepresentation = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName + "#" + <%= IdName(SourceTable) %>.ToString();
InBlock.gif    
return stringRepresentation.GetHashCode();
ExpandedSubBlockEnd.gif}

InBlock.gif
ExpandedBlockEnd.gif
#endregion
 


事情都已经做到这个份上也该收场了,虽然只是一些小技巧,也希望能给大家带来方便,提高生产效率。。。 :)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值