Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >NHibernate 多对多映射的数据更新

NHibernate 多对多映射的数据更新

作者头像
beginor
发布于 2020-08-10 06:36:11
发布于 2020-08-10 06:36:11
95300
代码可运行
举报
运行总次数:0
代码可运行

NHibernate 多对多映射的数据更新

最近在用 NHibernate 做多对多更新时突然发现 NHibernate 更新的策略很差, 对多对多关系的更新居然是先全部删除再插入全部数据, 感觉非常奇怪, 现在还原如下:

原来的实体类关系如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class User {

    public virtual int Id { get; set; }

    public virtual string Name { get; set; }

    public virtual ICollection<Role> Roles { get; set; }

    public User() {
        Roles = new HashSet<Role>();
    }
}

public class Role {

    public virtual int Id { get; set; }

    public virtual string Name { get; set; }

    public virtual ICollection<User> Users { get; set; }

    public Role() {
        Users = new HashSet<User>();
    }

}

即一个用户可以有多个角色, 一个角色也可以有多个人, 典型的多对多关系, 对应的映射代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class UserMapping : ClassMapping<User> {

    public UserMapping() {
        Table("[User]");

        Id(m => m.Id, map => {
            map.Column("[Id]");
            map.Type(NHibernateUtil.Int32);
            map.Generator(Generators.Identity);
        });

        Property(m => m.Name, map => {
            map.Column("[Name]");
            map.Type(NHibernateUtil.String);
        });

        Bag(
            m => m.Roles,
            map => {
                map.Table("[User_Role]");
                map.Key(k => { k.Column("[UserId]"); });
            },
            rel => {
                rel.ManyToMany(map => {
                    map.Class(typeof(Role));
                    map.Column("[RoleId]");
                });
            }
        );
    }
}

public class RoleMapping : ClassMapping<Role> {

    public RoleMapping() {
        Table("[Role]");

        Id(m => m.Id, map => {
            map.Column("[Id]");
            map.Type(NHibernateUtil.Int32);
            map.Generator(Generators.Identity);
        });

        Property(m => m.Name, map => {
            map.Column("[Name]");
            map.Type(NHibernateUtil.String);
        });


        Bag(
            m => m.Users,
            map => {
                map.Table("[User_Role]");
                map.Key(k => { k.Column("[RoleId]"); });
                map.Inverse(true);
            },
            rel => {
                rel.ManyToMany(map => {
                    map.Class(typeof(User));
                    map.Column("[UserId]");
                });
            }
        );

    }
}

数据库关系图如下:

当向用户添加或删除角色是, 发现更新的效率特别低, 代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
using (var session = sessionFactory.OpenSession()) {
    var user = session.Query<User>().First();

    var firstRole = user.Roles.First();
    user.Roles.Remove(firstRole);
    session.Update(user);

    var roleCount = session.Query<Role>().Count();
    var role = new Role { Name = "Role " + (roleCount + 1) };
    session.Save(role);

    user.Roles.Add(role);
    session.Update(user);

    session.Update(user);
    session.Flush();
}

上面的代码是将用户的第一个角色删除, 再添加一个新的角色, NHibernate 生成的 SQL 语句如下(仅包含对关系表 User_Role 的操作):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
DELETE FROM [User_Role] WHERE [UserId] = @p0;@p0 = 1 [Type: Int32 (0)]
INSERT INTO [User_Role]  ([UserId], [RoleId]) VALUES (@p0, @p1);@p0 = 1 [Type: Int32 (0)], @p1 = 2 [Type: Int32 (0)]
INSERT INTO [User_Role]  ([UserId], [RoleId]) VALUES (@p0, @p1);@p0 = 1 [Type: Int32 (0)], @p1 = 7 [Type: Int32 (0)]
INSERT INTO [User_Role]  ([UserId], [RoleId]) VALUES (@p0, @p1);@p0 = 1 [Type: Int32 (0)], @p1 = 6 [Type: Int32 (0)]
INSERT INTO [User_Role]  ([UserId], [RoleId]) VALUES (@p0, @p1);@p0 = 1 [Type: Int32 (0)], @p1 = 10 [Type: Int32 (0)]

居然是先将属于该用户的全部角色删除, 再添加一份新的进来, 完全无法接受, 反过来思考觉得肯定是自己的问题, 经过一番搜索 (Google), 发现 StackOverflow 上也有人问类似的问题, 并且最终在 NHibernate Tip: Use set for many-to-many associations 发现了解决方案, 将多对多的映射的 bag 改为用 set , 问题终于得到了解决, 改过后的映射如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Set(
    m => m.Roles,
    map => {
        map.Table("[User_Role]");
        map.Key(k => { k.Column("[UserId]"); });
    },
    rel => {
        rel.ManyToMany(map => {
            map.Class(typeof(Role));
            map.Column("[RoleId]");
        });
    }
);

UserMappingRoleMapping 中多对多映射全部改为 Set 之后, 上面的测试代码生成的 SQL 如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
DELETE FROM [User_Role] WHERE [UserId] = @p0 AND [RoleId] = @p1;@p0 = 1 [Type: Int32 (0)], @p1 = 8 [Type: Int32 (0)]
INSERT INTO [User_Role]  ([UserId], [RoleId]) VALUES (@p0, @p1);@p0 = 1 [Type: Int32 (0)], @p1 = 9 [Type: Int32 (0)]

在 NHibernate 参考文档的 19.5. Understanding Collection performance 中这样描述:

Bags are the worst case. Since a bag permits duplicate element values and has no index column, no primary key may be defined. NHibernate has no way of distinguishing between duplicate rows. NHibernate resolves this problem by completely removing (in a single DELETE) and recreating the collection whenever it changes. This might be very inefficient.

不只是多对多, 如果你的集合需要更新, NHibernate 推荐的是:

19.5.2. Lists, maps, idbags and sets are the most efficient collections to update

然而 bags 也不是一无是处:

19.5.3. Bags and lists are the most efficient inverse collections

Just before you ditch bags forever, there is a particular case in which bags (and also lists) are much more performant than sets. For a collection with inverse=”true” (the standard bidirectional one-to-many relationship idiom, for example) we can add elements to a bag or list without needing to initialize (fetch) the bag elements! This is because IList.Add() must always succeed for a bag or IList (unlike an ISet). This can make the following common code much faster.

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Parent p = sess.Load(id);
Child c = new Child();
c.Parent = p;
p.Children.Add(c);  //no need to fetch the collection!
sess.Flush();

由此可见, bag 在多对多映射更新时性能较差, 如果不需要更新,则可以放心使用, 在需要更新时则 set 是更好的选择。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
技能树Day01_R环境配置_直播课day01
(原网址http://www.bio-info-trainee.com/3727.html)
sheldor没耳朵
2024/07/17
1030
技能树Day01_R环境配置_直播课day01
三阴性乳腺癌表达矩阵探索之数据下载及理解
对文献解读的第三篇文章==Identification of Key Genes and Pathways in Triple-Negative Breast Cancer by Integrated Bioinformatics Analysis== 的分析过程进行重复
生信技能树
2020/10/26
2K0
三阴性乳腺癌表达矩阵探索之数据下载及理解
一. 生信入门环境搭建
大家把电脑配置一下,参考视频 https://www.bilibili.com/video/av80872684 根据这个做准备工作
白胖胖
2024/03/15
1570
rstudio软件无需联网但是
如下,实际上rstudio软件本身是无需联网的,需要联网的是R包安装等等中间过程,一开始就报错这是要劝退啊!!!
生信技能树jimmy
2020/03/30
1.1K0
生信学习者专属的R包安装终极指南
CRAN(Comprehensive R Archive Network)是R语言的官方网站,管理着20000+个R包,各种用途的都有。
用户11414625
2025/03/10
2110
生信学习者专属的R包安装终极指南
R包各大镜像纷纷宕机
这个对初学者来说,的确很难理解,如果你参加学习班的话,就有讲师和助教团队协助解决了(感兴趣的可以点击下面两个课程)
生信技能树
2020/04/20
1.4K0
关于r包安装到哪里了以及如何看自己的r包数量
一般来说,如果是网络好的话,上面的代码肯定是丝滑运行的。但是绝大部分小伙伴都是在中国大陆地区,因为有个别r包依赖于几十个其它包,比如这个大名鼎鼎的clusterProfiler,如果你网络不好,很有可能它本来是可以自动安装各种依赖的因为网络问题终止。所以如果你在library(clusterProfiler)报错很正常的。这个时候你需要单独的安装它缺失的依赖包即可,比如如果你的报错告诉你你缺 GO.db,它是一个R包,你只需要装它,仿写你运行的脚本里面BiocManager开头的代码,把引号里面的词换成GO.db就行。
生信技能树
2024/11/21
1100
关于r包安装到哪里了以及如何看自己的r包数量
R语言确实会蛮耗费磁盘空间哦
但是这个过程中,总有学员抱怨到自己的C盘空间不够, 我们的R语言会不会耗费磁盘空间。我们通常是先安慰一下,R仅仅是几十个M而已,各种R包通常是几个M,不会太耗费磁盘空间的。
生信技能树
2020/09/14
1.5K0
01 Ubuntu 22.04 安装Anaconda+配置R语言&RStudio
提示You can undo this by running conda init --reverse $SHELL 输入 yes
用户11266652
2024/10/08
6190
01 Ubuntu 22.04 安装Anaconda+配置R语言&RStudio
使用miRNAtap数据源提取miRNA的预测靶基因结果
今天我们比较一下另外的一个miRNAtap包,与multiMiR的结果的一致性,以及两个数据库的差异!首先安装miRNAtap包,代码如下:
生信技能树
2020/04/21
1.6K0
使用miRNAtap数据源提取miRNA的预测靶基因结果
OSCA单细胞数据分析笔记-2—R与Bioconductor
提高下载安装包的速度。如下分别为bioconductor与CRAN选择了清华的镜像源
生信技能树jimmy
2021/04/16
9510
OSCA单细胞数据分析笔记-2—R与Bioconductor
「Workshop」第二十四期 GEO芯片数据处理-1
图中的Dim1~Dim2分别是指主成分1和主成分2, Dim1代表解释数据变化趋势最多的主成分,Dim2则是解释变化趋势第二多的主成分。括号内的百分比则是代表了不同主成分所能解释数据变化趋势的比例。中心位置的大点代表该组的位置。一个点是一个sample,点距离代表相似性(差异)。
王诗翔呀
2020/11/20
1.4K0
「Workshop」第二十四期 GEO芯片数据处理-1
根据坐标在基因组上面拿到碱基序列来设计引物
如果仅仅是一两个位点, 我们可以很容易通过各种各样的网页工具去查询到它的序列信息,但是高通量测序的结果往往是成千上万的,就算是节省成本,一般来说也会挑选100个左右的位点拿去设计引物进行sanger测序,一个个网页查询工作量有点大,这个时候就可以使用代码实现批量查询。
生信技能树
2020/10/26
1.6K0
根据坐标在基因组上面拿到碱基序列来设计引物
【紧急通知】下载R包却联网失败?初学者的痛
我们给出来的解决方案,仍然是;之前研发好的 url.method 这个配置的解决方案;
生信技能树
2020/04/14
1.1K0
【紧急通知】下载R包却联网失败?初学者的痛
BiocManager安装R包失败——Bioconductor version cannot be validated
前言1. 报错内容2. 解决方案3. 安装R包模板3.1 镜像设置3.2 下载方式设置3.3 安装R包4. 永久保存镜像设置后记
生信技能树
2023/02/27
13.3K1
BiocManager安装R包失败——Bioconductor version cannot be validated
R语言入门mac——RStudio安装➕R包安装【附安装链接】[通俗易懂]
RStudio需要R 3.0.1+ 下载链接:https://cran.rstudio.com/
全栈程序员站长
2022/09/14
3.1K0
clusterProfiler到底有多难安装呢
但是最近频繁看到粉丝留言表明安装clusterProfiler包失败,这个clusterProfiler是大名鼎鼎的Y叔开发,基本上是每个做生物信息学数据分析的人都会使用它的,做超几何分布检验(富集分析),而且内置了很多数据库,好用的函数。
生信技能树
2020/07/30
3.9K0
clusterProfiler到底有多难安装呢
在Ubuntu下安装单细胞3大R包
通常来说,很多R包的安装对R版本是有要求的,比如BiocManager需要 R (≥ 3.5.0),但是并不需要最新版R语言。
生信技能树
2019/07/26
2.5K0
Y叔的clusterProfiler承包了富集分析结果的可视化
见Y叔的网络在线书籍《clusterProfiler: universal enrichment tool for functional and comparative study》的 Chapter 12 Visualization of Functional Enrichment Result , 自己简单搜索就可以直达这个在线书籍的链接:
生信技能树
2021/07/29
1.8K0
史上最贴心R包安装示范视频
1990年,伊丽莎白·牛顿在斯坦福大学通过研究一个简单的游戏获得了心理学博士学位。在这个游戏中,她把参与者分为两种角色:“敲击者”和“听众”。敲击者拿到一张25首名曲的单子,包括《祝你生日快乐》这种旋律简单的歌曲。每位敲击者挑选一首,把节奏敲给听众听(通过敲桌子)。听众的任务是根据敲击的节奏猜出歌曲。
生信技能树
2018/08/16
1.5K0
史上最贴心R包安装示范视频
相关推荐
技能树Day01_R环境配置_直播课day01
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验