文档章节

NHibernate 多对多映射的数据更新

李朝强
 李朝强
发布于 2016/02/17 12:33
字数 939
阅读 34
收藏 1
点赞 1
评论 0

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

原来的实体类关系如下:

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>();
    }

}

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

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]");
                });
            }
        );

    }
}

数据库关系图如下:

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

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的操作):

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, 问题终于得到了解决, 改过后的映射如下:

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]");
        });
    }
);

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

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.

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是更好的选择。

本文转载自:http://beginor.github.io/2015/03/17/nhibernate-many-to-many-update.html

共有 人打赏支持
李朝强
粉丝 80
博文 269
码字总数 137210
作品 0
郑州
产品经理
NHibernate从入门到精通系列(10)——多对多关联映射

内容摘要 单向多对多关联映射 双向多对多关联映射 一、单向多对多关联映射 1.1 多对多关联映射描述 众所周知,持久化类的有三种对应关系:“一对一”、“一对多(多对一)”和“多对多”。在...

长平狐 ⋅ 2012/06/11 ⋅ 0

Visual Entity 下载列表(最新版为 1.8.0 ,已支持Postgre&DB2,即将支持 Sysbase ASE)

发展线路: 2.x 将支持的功能有: 1、增加重构功能。当修改类或属性名称,提示并进行重构以更新所有引用。 2、允许将自定义的特性放在属性窗口中,以方便编辑。 3、Web Services 代码生成。 ...

长平狐 ⋅ 2013/06/17 ⋅ 1

ORM数据访问组件--ELinq

ELinq 是一个轻量简单易用的开源Linq ORM数据访问组件,支持Nullable类型和枚举类型,对Linq的谓词提供了完美的支持,旨在让绝大部份的主流数据库都使用 Linq 来进行程序开发,让开发人员访问...

netcasewqs ⋅ 2012/12/17 ⋅ 2

Nhibernate 多对多级联更新

问题是这样的,有两个表:文章(Article)和分类(Lable),这两者之间的关系是多对多关联,如果你用Nhibernate来保存数据的话非常的好操作,新建Article,然后把Lable值赋值给Article的Lables(这...

awbeci ⋅ 2014/03/21 ⋅ 0

从Prism中学习设计模式之Event Aggregator 模式

Event Aggregator 模式定义:渠道事件从多个对象通过一个单一的对象来简化clients的注册。 结构图: Prism的Event Aggregator 模式:Event Aggregator允许多对象定位和发布、订阅事件。 我们...

andrewniu ⋅ 05/29 ⋅ 0

nhibernate入门使用经验

最近项目中用到nhibernate,学到了一点。就在这里写一点。 nhibernate是java世界的hibernate的.net版本,其工作原理和hibernate一样的。我们需要用hbm的文件来描述数据表及表之间的关系。刚好...

mikelij ⋅ 2013/07/13 ⋅ 0

NHibernate从入门到精通系列(1)——NHibernate概括

内容摘要 NHibernate简介 ORM简介 NHibernate优缺点 一、NHibernate简介 什么是?NHibernate?NHibernate是一个面向.NET环境的对象/关系数据库映射工具。对象/关系数据库映射(object/relati...

长平狐 ⋅ 2012/06/11 ⋅ 0

在.NET数据库访问方面的Dapper类库介绍

在开发带数据库的.NET系统中我使用过各种方式,包括直接使用ADO.NET、使用基于ADO.NET封装的各类工具(其中有自己封装的)、还有各类ORM类库,比如NHibernate、MyBatisNet、Linq to SQL、Ent...

周金桥 ⋅ 05/08 ⋅ 0

Vue 绑定class的生效先后问题

想请问下绑定class的生效的先后问题。 这个是布局 这个是样式 界面效果是这样的 结果和我想的不一样,我以为:class会覆盖class,结果正好相反,求解惑和解决方案。 ps 如果用:style是可以的...

LoaRZhou ⋅ 03/19 ⋅ 0

比较 NHibernate 和实体框架

葡萄牙的一位开发者 Ricardo Peres 最近发布了一篇文章,以看起来无偏见的形式对领先的两种 .NET ORM:NHibernate 和实体框架进行了比较。 我们建议考虑使用这两种框架的人都应该读下他的文章...

墙头草 ⋅ 2012/06/18 ⋅ 4

没有更多内容

加载失败,请刷新页面

加载更多

下一页

【elasticsearch】 随笔 Date datatype

一。时间类型的本质 首先json是没有时间类型的,对于es来说,时间类型的标示可以是下面三种情况 1.一个时间格式的字符串,如:"2014-11-27T08:05:32Z","2015-01-01" or "2015/01/01 12:10:3...

xiaomin0322 ⋅ 29分钟前 ⋅ 0

阿里云资源编排ROS使用教程

阿里云资源编排ROS详细内容: 阿里云资源编排ROS使用教程 资源编排(Resource Orchestration)是一种简单易用的云计算资源管理和自动化运维服务。用户通过模板描述多个云计算资源的依赖关系、...

mcy0425 ⋅ 31分钟前 ⋅ 0

适配器设计模式

1、适配器模式 把一个类的接口变换成客户端所期待的另一种接口 使原本因接口不匹配而无法在一起工作的两个类能够在一起工作 分为类的适配器模式和对象的适配器模式 2、类适配器模式 类的适配...

职业搬砖20年 ⋅ 36分钟前 ⋅ 0

npm操作报错 _stream_writable.js:61

有一天 不知道什么原因(估计和node的版本有关),无论你做什么npm的操作 都会报错/usr/local/lib/node_modules/npm/node_modules/readable-stream/lib/_stream_writable.js:61 这时候只要执...

lilugirl ⋅ 39分钟前 ⋅ 0

Eclipse安装插件的几种方式

Eclipse魅力之一就是支持可扩展的插件,来丰富自身的功能,这种方式也是建立在开源思想之上的。具体使用什么方式去安装插件,要看我们拿到的是什么。 1. 拿到的是一串URL,如http://subclips...

GordonNemo ⋅ 42分钟前 ⋅ 0

div图片叠加

css实现代码如下: <div style="position: relative;"><!--这个层为外面的父层,需设置相对位置样式--> <div style="position: absolute;"><!--子层,需设置绝对位置样式--> <i......

niithub ⋅ 43分钟前 ⋅ 0

作用域slot

如果父组件需要使用子组件中的内容怎么办,比如父组件需要控制子组件的显示 <div id="root"><child><template slot-scope="props"><h1>{{props.item}} <div>编辑</div></h1><......

金于虎 ⋅ 46分钟前 ⋅ 1

HongHu commonservice-eureka 项目构建过程

上一篇我们回顾了关于 spring cloud eureka的相关基础知识,现在我们针对于HongHu cloud的eureka项目做以下构建,整个构建的过程很简单,我会将每一步都构建过程记录下来,希望可以帮助到大家...

明理萝 ⋅ 49分钟前 ⋅ 1

xml和对象的相互转化

@Data//setter和getter方法,toString和equals,hashcode方法@EqualsAndHashCode//代表重写equals和hashcode方法@XmlAccessorType(XmlAccessType.FIELD)public class Classroom {@X......

拐美人 ⋅ 49分钟前 ⋅ 0

tableView cell的高度 分组头部尾部的高度 自适应

@property (nonatomic) CGFloat rowHeight; // default is UITableViewAutomaticDimension@property (nonatomic) CGFloat sectionHeaderHeight; // default is UITableViewA......

娜一片蓝色星海 ⋅ 50分钟前 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部