NHibernate联合主键详细示例

使用NHibernate实现一对多,多对一的关联很是简单,可如果要用复合主键实现确实让人有些淡淡的疼。虽然很淡疼但还是要去抹平这个坑,在下不才,愿意尝试。

以示例进入正文,源码下载地址

一、数据表关系图

很明显,他是一个自引用数表,实现无限级树结构的存储。

二、关键步骤

  • 注解如何实现复合主键

根据官方文档说明,联合主键最好是一个独立的类,需要重载Equals和GetHashCode方法,且标记为可序列化。代码如下:

[Serializable]
public class BaseInfo
{
    public virtual string Id { get; set; }
    public virtual string GroupNumber { get; set; }

    public override bool Equals(object obj)
    {
        var baseInfo = obj as BaseInfo;
        if (baseInfo == null)
        {
            return false;
        }

        return baseInfo.Id == this.Id && baseInfo.GroupNumber == this.GroupNumber;
    }
    public override int GetHashCode()
    {
        return base.GetHashCode(); 
    }
}
  • 子类配置好联合主键
[CompositeId(0, Name = "BN")]
[KeyProperty(1, Name = "Id", Column = "Id", TypeType = typeof(string))]
[KeyProperty(2, Name = "GroupNumber", Column = "GroupNumber", TypeType = typeof(string))]
public virtual BaseInfo BN { get; set; }

说明: 1.实现为引用BaseInfo类,而不是继承.

  • 实现一对 和 多对一的映射

这步没有多大难度,主要处理好注解的顺序即可,以及OneToMany时联合主键如何设置的问题.示例代码如下:

[Bag(0, Name = "Childs", Cascade = "all", Lazy = CollectionLazy.False, Inverse = true)]
[Key(1)]
[Column(2, Name = "ParentId")]
[Column(3, Name = "GroupNumber")]
[OneToMany(4, ClassType = typeof(Foo))]
public virtual IList<Foo> Childs { get; set; }

[ManyToOne(0, Name = "Parent", ClassType = typeof(Foo))] 
[Column(1, Name = "ParentId")]
[Column(2, Name = "GroupNumber")]
public virtual Foo Parent { get; set; }

三、出错了,有Bug

  • childs没有数据

重载的GetHashCode方法有问题,返回值应该是联合主键HashCode,优化后的实现如下:

public override int GetHashCode()
{
    return (this.Id + "|" + this.GroupNumber).GetHashCode(); //判断缓存是否存在,已此作为Key
}
  • 插入数据时报错,提示SqlParameterCollection的索引无效[索引溢出错误]

原因,最初在设计Parent的时候,与联合主键共用了一个字段GroupNumber,导致在NHibernate做映射转换的时候会多计算出一个需要填充的值,但SqlParameterCollection中又少一个位置。优化代码如下:

//外键与联合主键不要共用字段
[ManyToOne(0, Name = "Parent", ClassType = typeof(Foo))] 
[Column(1, Name = "ParentId")]
[Column(2, Name = "ParentGroupNumber")]
public virtual Foo Parent { get; set; }

说明: 1.由于联合外键与联合主键共用了一个字段,导致映射出错

四、终于实现了,总结

  • 类都必须可以序列化,也就是要还serializable标注
  • 继承BaseInfo实现联合主键(不推荐使用)

在Save时,如果用session.merge方法组合缓存与修改对象,返回值的主键会为Null

  • 联合主键与联合外键字段不能重复,也不能共用
  • 注意重载的GetHashCode和Equals方法
  • GetHashCode返回实例的惟一标识
  • Equals判断是否相同实例的具体实现

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏郭少华

Spring boot Mybatis-XML方式通用Mapper插件(七)

特别注意,如果使用了1.2.0以上版本 @MapperScan 注解,请使用 tk.mybatis.spring.annotation.MapperScan 注...

1.1K10
来自专栏xingoo, 一个梦想做发明家的程序员

【设计模式】—— 单例模式Singleton

  模式意图   保证类仅有一个实例,并且可以供应用程序全局使用。为了保证这一点,就需要这个类自己创建自己的对象,并且对外有公开的调用方法。   模式结构 ? ...

19670
来自专栏不想当开发的产品不是好测试

mysql @value := 用法

背景 有这么一张表,记录名字和分数,现在需要按照成绩排名,存在并列名次的情况 ? 解决方法 思路:按照score从大到小排序,第一行数据就是第一名,第二行就是第...

29480
来自专栏Android干货

浅谈Android数据库DBFlow

27250
来自专栏C/C++基础

MySQL存储过程

可惜啊!MySQL目前并不支持在SQL语句中存在流控制语句,例如上面的IF NOT EXISTS THEN END IF;让人痛心疾首。但是我们可以使用存储过程...

13510
来自专栏信安之路

sqlmap自带的tamper你了解多少?

sqlmap 是一款注入神器广为人知,里面的 tamper 常常用来绕过 WAF ,很实用的模块,但是却常常被新手忽略(比如我),今天就整理总结一下 tampe...

11500
来自专栏数据库

MySQL与InnoDB(下)-B+树与索引

本节主要讲对记录的查找。关于数据库查询,首先要从查询算法的角度优化,从顺序查找、二分查找再到二叉树查找。但每一种算法只能用于特定的数据结构上,比如二分查找必须有...

27180
来自专栏lonelydawn的前端猿区

oracle细节

01、SQL查询语句不区分大小写,但是数据区分 02、where从句中Name=null是查询不到结果的,必须用 is null 03、union去重,unio...

24580
来自专栏黑白安全

sqlmap的tamper详解

sqlmap 是一款注入神器广为人知,里面的 tamper 常常用来绕过 WAF ,很实用的模块,但是却常常被新手忽略(比如我),今天就整理总结一下 tampe...

37230
来自专栏李智的专栏

Mysql进阶

1) 内连接 select a.*,b.* from a inner join b on a.id=b.parent_i...

8110

扫码关注云+社区

领取腾讯云代金券