NHibernate's concept of 'inverse' in relationships is probably the most often discussed and misunderstood mapping feature. When I was learning NHibernate, it took me some time to move from "I know where should I put 'inverse' and what then happens" to "I know why do I need 'inverse' here and there at all". Also now, whenever I'm trying to explain inverses to somebody, I find it pretty hard.

There are a lot of explainations over the net, but I'd like to have my own one. I don't think that the others are wrong, it'll just help me arrange my own understanding and if anyone else take advantage of this, that's great.

Where do we use inverse?

First, some widely-known facts, next we'll elaborate on few of them.

  • Inverse is a boolean attribute that can be put on the collection mappings, regardless of collection's role (i.e. within one-to-many, many-to-many etc.), and on join mapping.
  • We can't put inverse on other relation types, like many-to-one or one-to-one.
  • By default, inverse is set to false.
  • Inverse makes little sense for unidirectional relationships, it is to be used only for bidirectional ones.
  • General recommendation is to use inverse="true" on exactly one side of each bidirectional relationship.
  • When we don't set inverse, NHProf will complain about superfluous updates.

What does it mean for a collection to be 'inverse'?

The main problem in understanding 'inverse' is it's negating nature. We're not used to setting something up in order to NOT take an action. Inverse set to true means "I do NOT maintain this relationship". Hence, inverse set to false means "I DO maintain this relationship".

It'll be much more understandable if we could go to the opposite side of the relationship and be positive there: "This side maintains the relationship" and NHibernate would automatically know that the other side doesn't (*). But it is implemented as it is - we have to live with inverse's negative character.

Each relationship is represented in the database as an identifier of a related table row in the foreign key column at 'many' side. Why at 'many' side? Because that's how we do relationships in the relational databases. The column "holding" the association is always at 'many' side. It's not possible to keep the association at 'one' side because we'd have to insert many values into one database field somehow.

So what does it mean for a collection in NHibernate to maintain the relationship (inverse="false")? It means to ensure that the relation is correctly represented in the database. If the Comments collection in the Post object is responsible for maintaining the relationship, it has to make sure all its elements (comments) have foreign keys set to post's id. In order to do that, it issues a SQL UPDATE statement for each Comment, updating its Post reference. It works, the relationship is persisted correctly, but these updates often do not change anything and can be skipped (for performance reasons).

Inverse="true" on a collection means that it should not take care whether the foreign keys in the database are properly set. It just assumes that some other party will take care of it. What do we gain? We have no superfluous UPDATE statements. What can we lose? We have to be sure that the second side actually takes over the responsibility of maintaining the association. If it doesn't, nobody will and we'll be surprised that our relationship is not persisted at all (NHibernate will not throw an error or so, it won't guess that it's not what we've expected).

When should we set inverse="true"?

Let's consider one-to-many first. Our relationship must be bidirectional and have entities (not value types) at both sides for inverse to make sense. Other side ('many' side) is always active, we can't set inverse on many-to-one. This means that we should put inverse="true" on the collection, provided that:

  • our collection is not explicitly ordered (like <list>) - it is i.e. <bag> or <set>; ordered lists have to be active in order to maintain the ordering correctly; 'many' side doesn't know anything about the ordering of collection at 'one' side
  • we actually set the relationship at 'many' side correctly

Consider the example:

public class Post
{
public virtual int Id { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
} public class Comment
{
public virtual int Id { get; set; }
public virtual Post Post { get; set; }
public virtual string Text { get; set; }
} // ... var comment = new Comment() { Text = "the comment" };
session.Persist(comment);
post.Comments.Add(comment);

We are not setting Post property in Comment class as we may expect NHibernate will handle that as we append our comment to the collection of comments in particular Post object (**). If the post.Comments collection is not inverse, it will actually happen, but quite ineffectively:

We've inserted null reference first (exactly as it was in our code) and then, as the collection is responsible for maintaining the relationship (inverse="false"), the relationship was corrected by separate UPDATE statement. Moreover, in case we have not null constraint on Comment.Post_id (which is actually good), we'll end up with exception that we can't insert null foreign key value.

Let's see what happens with inverse="true":

There's no error, but the comment is actually not connected to the post, despite we've added it to a proper collection. But using inverse, we've explicitly turned off maintaining the relationship by that collection. And as we don't set the relationship onComment side, noone does.

The solution of course is to explicitly set comment's Post property. It is good from object model perspective, too, as it reduces the amount of magic in our code - what we've set is set, what we haven't set is not set magically.

var comment = new Comment() { Text = "the comment", Post = post };
session.Persist(comment);
post.Comments.Add(comment);

Much better now:

Time for many-to-many. Again, inverse makes sense only when we've mapped both sides. We have to choose one side which is active and mark the second one as inverse="true". Without that, when both collections are active, both try to insert a tuple to an intermediate table many-to-many needs. Having duplicated tuples makes no sense in most cases. For some suggestions how to choose which side is better in being active, see my post from December.

To sum up

Left side Right side Inverse?
one-to-many not mapped makes no sense - left side must be active
one-to-many many-to-one right side should be active (left with inverse="true"), to save on UPDATEs
(unless left side is explicitly ordered)
many-to-many not mapped makes no sense - left side must be active
many-to-many many-to-many one side should be active (inverse="false"), the other should not (inverse="true")

______

(*) There are of course reasons why NHibernate doesn't do assumptions about other sides of relationships like that. The first one is to maintain independence between mappings - it will be cumbersome if change in mapping A modifies the B behaviour. The second one are ordered collections, like List. The ordering can be automatically kept by NHibernate only when collection side is active (inverse="false"). If the notion of being active is managed on the other side only, changing the collection type from non-ordered to ordered would require changes in both mappings.

(**) Note that inverse is completely independent from cascading. We can have cascade save on collection and it does not affect which side is responsible for managing the relationship. Cascade save means only that when persisting Post object, we're also persisting all Comments that were added to the collection. They are inserted with null Post value and UPDATEd later or inserted with proper value in single INSERT, depending on object state and inverse setting, as described above.

NHibernate's inverse - what does it really mean?的更多相关文章

  1. 使用NHibernate(10) -- 补充(inverse && cascade)

    1,inverse属性的作用: 只有集合标记(set/map/list/array/bag)才有invers属性: 以set为例,set的inverse属性决定是否把对set的改动反应到数据库中去,i ...

  2. NHibernate之映射文件配置说明

    NHibernate之映射文件配置说明 1. hibernate-mapping 这个元素包括以下可选的属性.schema属性,指明了这个映射所引用的表所在的schema名称.假若指定了这个属性, 表 ...

  3. 【翻译】Fluent NHibernate介绍和入门指南

    英文原文地址:https://github.com/jagregory/fluent-nhibernate/wiki/Getting-started 翻译原文地址:http://www.cnblogs ...

  4. [NHibernate]集合类(Collections)映射

    系列文章 [Nhibernate]体系结构 [NHibernate]ISessionFactory配置 [NHibernate]持久化类(Persistent Classes) [NHibernate ...

  5. [NHibernate]关联映射

    系列文章 [Nhibernate]体系结构 [NHibernate]ISessionFactory配置 [NHibernate]持久化类(Persistent Classes) [NHibernate ...

  6. [NHibernate]Parent/Child

    系列文章 [Nhibernate]体系结构 [NHibernate]ISessionFactory配置 [NHibernate]持久化类(Persistent Classes) [NHibernate ...

  7. [NHibernate]一对多关系(级联删除,级联添加)

    目录 写在前面 文档与系列文章 一对多关系 一个例子 级联删除 级联保存 总结 写在前面 在前面的文章中,我们只使用了一个Customer类进行举例,而在客户.订单.产品中它们的关系,咱们并没有涉及, ...

  8. [NHibernate]立即加载

    目录 写在前面 文档与系列文章 立即加载 一个例子 总结 写在前面 上篇文章介绍了nhibernate延迟加载的相关内容,简单回顾一下延迟加载,就是需要的时候再去加载,需要的时候再向数据库发出sql指 ...

  9. [Nhibernate]二级缓存(一)

    目录 写在前面 文档与系列文章 二级缓存 Nhibernate二级缓存提供程序 一个例子 总结 写在前面 上篇文章介绍了nhibernate中一级缓存的相关内容,一级缓存过期时间和ISession对象 ...

随机推荐

  1. docker的四种网络模式

    /* 1. host模式 : docker run 使用 --net=host指定 docker使用的网络实际上和宿主机一样 2. container模式: 使用 --net=container:co ...

  2. Power of Two & Power of Three & Power of Four

     Check Power of 2 Using O(1) time to check whether an integer n is a power of 2. Example For n=4, re ...

  3. 2.7 编程之美--最大公约数的3种解法[efficient method to solve gcd problem]

    [本文链接] http://www.cnblogs.com/hellogiser/p/efficient-method-to-solve-gcd-problem.html [题目] 求两个正整数的最大 ...

  4. 16.O(logn)求Fibonacci数列[Fibonacci]

    [题目] log(n)时间Fib(n),本质log(n)求a^n. [代码]  C++ Code  12345678910111213141516171819202122232425262728293 ...

  5. 转 MYSQL学习(一)

    第一期主要是学习MYSQL的基本语法,陆续还有第二.第三.第四期,大家敬请期待o(∩_∩)o 语法的差异 我这里主要说语法的不同 1.默认约束 区别:mysql里面DEFAULT关键字后面是不用加括号 ...

  6. Greedy:三角形问题

    题目大意:有n根长度的为a1,a2....an的棒子,如果棒子可以组成三角形,求这些棒子能组成的三角形的最大周长? 这一题,一般人只能想到三重循环,当然我们是CS专业的,不能这样想,其实这题可以用DP ...

  7. codeforces A. Strange Addition 解题报告

    题目链接:http://codeforces.com/problemset/problem/305/A 题目意思:给出一个序列,需要从中选择一些数,这些数需要满足:任意的两个数中每一位至少有一个数满足 ...

  8. NEFU 1142 表哥的面包

    表哥的面包 Problem:1142 Time Limit:1000ms Memory Limit:65535K Description 可爱的表哥遇到了一个问题,有一个长为N(1≤N≤10^18)的 ...

  9. C语言中的break、continue和goto三者的区别与用法

    exit的话是在stdlib的头文件里面定义的,他是的程序退出,正如exit的字面意思一样break的话是终止当前循环继续进行循环后面的语句,但是程序照样还在进行只能while switch for中 ...

  10. 菜鸟学Linux命令:bg fg jobs命令 任务管理

    jobs命令 jobs命令用于查看当前终端后台运行的任务 注意和ps的区别: ps命令用于查看瞬间进程的动态 通过一个实例可以理解它们之间的区别,依次执行如下命令:vim & //后台执行vi ...