NHibernate and inverse: my view on that subject

NHibernate’s inverse concept is one of  most discussed mappings features (if not the most!) defined by it. In practice, you’ll be using the inverse when you need to set the owner of a “relationship”. Before taking a dive into it and how it affects the way NH generates SQL and manages relationships between entities, here are some facts about inverse:

  • It’s a Boolean attribute used only in collection  and join mappings;
  • By default, it’s set to false;
  • It only makes sense when you’re configuring bidirectional relationships between entities;
  • You should only set inverse to true on one of the sides of the relationship;
  • Not setting it on any of the sides will generate superfluous SQL instructions.

Ok, before trying to understand what this means, lets start with a RDE (ie, a really dumb example): Blogs and posts will do it for now!

public class Blog {
    public Int32 BlogId { get; set; }
    public Int32 Version { get; set; }
    public String Description { get; set; }
    private IList<Post> _posts = new List<Post>();

    public IEnumerable<Post> Posts {
        get { return new ReadOnlyCollection<Post>(_posts); }
        set { _posts = new List<Post>(value); }
    }

    public void AddPost(Post post) {
        post.Blog = this;
        _posts.Add(post);
    }

    public void RemoveAll() {
        foreach (var post in _posts) {
            post.Blog = null;
        }
        _posts.Clear();
    }
}

public class Post {
    public Int32 PostId { get; set; }
    public String Description { get; set; }
    public Blog Blog { get; set; }
}

As you can see, there’s nothing too fancy going on here. Each blog has a collection of posts (with each post referencing back its parent blog) and you’ll typically use the AddPost method to add a new Post. After building the classes, we can concentrate in building the mappings. And nothing like Fluent NHibernate to give us a hand. Since I’m not really a great fan of automapping, here’s the code I’ve written to get us started:

public class PostMapping : ClassMap<Post> {
    public PostMapping() {
        Table("Post");
        Not.LazyLoad();
        Id(p => p.PostId)
            .GeneratedBy.Identity()
            .Default(0);
        Map(p => p.Description);
        References(p => p.Blog, "BlogId")
            .Not.LazyLoad();
    }
}

public class BlogMapping : ClassMap<Blog> {
    public BlogMapping() {
        Table("Blog");
        Not.LazyLoad();

        Id(b => b.BlogId)
            .GeneratedBy.Identity()
            .Default(0);
        Map(b => b.Description);
        Version(b => b.Version);
        HasMany(b => b.Posts)
            .Access.CamelCaseField(Prefix.Underscore)
            .AsBag()
            .Cascade.AllDeleteOrphan()
            .Not.LazyLoad()
            .KeyColumn("BlogId");
    }
}

This is more than enough for you to reproduce the tables (if you’re interested in running the examples). I’m also not showing the code I’ve written to create NH’s session factory from these mappings (Fluent’s wiki shows you how to get started, so I’m not repeating it here). Having said that, lets start with the code required to create a new Blog:

private static void CreateNewBlog() {
    var sessionFactory = SessionFactory.CreateSessionFactory();
    using (var session = sessionFactory.OpenSession()) {
        using (var tran = session.BeginTransaction()) {
            var blog = new Blog() {Description = "Testing blog"};
            var post = new Post() {Description = "Post 1"};
            blog.AddPost(post);
            session.SaveOrUpdate(blog);
            tran.Commit();
        }
    }
}

If we were writing the SQL by hand, we could probably agree that 2 SQL instructions would be more than enough for getting the entities stored in the database: we’d start by saving Blog, getting its ID and then we’d insert all its associated posts into the Post’s table (oh, and since Post is also an entity, with probably get its ID too since I’m using autogenerated IDs – not the best of choices for real world projects, but more than enough for our current discussion). The following snippet shows the SQL generated by NHibernate to save the previous objects to the database:

INSERT INTO Blog (Version, Description) VALUES (@p0, @p1);@p0 = 1 [Type: Int32 (0)], @p1 = ‘Testing blog’ [Type: String (0)]    
select @@IDENTITY    
INSERT INTO Post (Description, BlogId) VALUES (@p0, @p1);@p0 = ‘Post 1′ [Type: String (0)], @p1 = 4 [Type: Int32 (0)]    
select @@IDENTITY    
UPDATE Post SET BlogId = @p0 WHERE PostId = @p1;@p0 = 4 [Type: Int32 (0)], @p1 = 14 [Type: Int32 (0)]    

The first SQL instructions seem to be going as expected, but then there’s an extra update instruction right at the end which, at first sight, makes no sense: we’re updating the Post table’s BlogId field to the ID of the inserted post and this isn’t really needed.

If we change the mappings by setting the inverse flag on the Blog’s Posts mapping, then the results are completely different:

public class BlogMapping:ClassMap<Blog> {
    public BlogMapping() {
        Table("Blog");
        Not.LazyLoad();

        Id(b => b.BlogId)
            .GeneratedBy.Identity()
            .Default(0);
        Map(b => b.Description);
        Version(b => b.Version);
        HasMany(b => b.Posts)
            .Access.CamelCaseField(Prefix.Underscore)
            .AsBag()
            .Cascade.AllDeleteOrphan()
            .Not.LazyLoad()
            .KeyColumn("BlogId")
            .Inverse();
    }
}

And here’s NH’s generated SQL for the previous insertion:

INSERT INTO Blog (Version, Description) VALUES (@p0, @p1);@p0 = 1 [Type: Int32 (0)], @p1 = ‘Testing blog’ [Type: String (0)]    
select @@IDENTITY    
INSERT INTO Post (Description, BlogId) VALUES (@p0, @p1);@p0 = ‘Post 1′ [Type: String (0)], @p1 = 5 [Type: Int32 (0)]    
select @@IDENTITY    

Oh yes, now we’re in business! But what’s going on here? What does Inverse do? 

IMO, the main problem in getting the inverse attribute is it’s “negative” meaning. If we set inverse to true, we’re saying that this entity is not responsible for maintaining the relationship. On the other hand, setting inverse to false does mean that that entity is the one responsible for managing the relationship. So, when we added the Inverse call to the Blog side, we’re saying that the Blog entity isn’t responsible for managing its relationship with Post (at the database level). In this case, managing the relationship should be done on the Post’s side. This is a little bit counterintuitive, but if we take a step back and recall how things work at the database level, it might make sense.

In a database, each relationship is represented by a foreign key  on the many side (going back to our RDE, the Post table will have a foreign key – BlogId – which references the primary key of the Blog table). Taking this into consideration does help understand the invert attribute: we can insert entries in the Blog table without caring about its associated Posts, but we can’t really add a Post without setting its BlogId foreign key. And that’s why the inverse attribute we’ve set in the Blog side of the mappings did eliminate the superfluous UPDATE call: with it, Blog is no longer responsible for making sure that the foreign key of each post is correctly set up (that will be taken care of by Post).

It goes without saying that in order for the persistence of the Blog aggregate to work, Post must have all the required info so that it doesn’t violate the foreign key rule when it persists itself to the database. And in fact, my initial code already does all the necessary setup. Notice that adding a Post to a Blog will always set its Blog field to its parent (the owning Blog) and that that field is defined as a reference to the Blog table on the mapping file.

Before ending, one extra note: did you notice that Cascade.AllDeleteOrphan() call in the mappings? Well, that’s really important when you want to remove a Post that has previously been persisted from the database. Here’s some code that will help you understand what I’m saying:

private static void UpdateBlog() {
    var sessionFactory = SessionFactory.CreateSessionFactory();
    using (var session = sessionFactory.OpenSession()) {
        using (var tran = session.BeginTransaction()) {
            var blog = session.Get<Blog>(2);
                    
            blog.RemoveAll();
            var newPost = new Post {Description = "Post 2"};
            blog.AddPost(newPost);

            tran.Commit();
        }
    }
}

After loading a previously saved Blog, I’m removing all its posts and adding a new one. Here’s the log for the generated SQL when you don’t delete orphan’s entities:

SELECT blog0_.BlogId as BlogId0_0_, blog0_.Version as Version0_0_, blog0_.Description as Descript3_0_0_ FROM Blog blog0_ WHERE blog0_.BlogId=@p0;@p0 = 2 [Type: Int32 (0)]    
SELECT posts0_.BlogId as BlogId1_, posts0_.PostId as PostId1_, posts0_.PostId as PostId1_0_, posts0_.Description as Descript2_1_0_, posts0_.BlogId as BlogId1_0_ FROM Post posts0_ WHERE posts0_.BlogId=@p0;@p0 = 2 [Type: Int32 (0)]    
INSERT INTO Post (Description, BlogId) VALUES (@p0, @p1);@p0 = ‘Post 2′ [Type: String (0)], @p1 = 2 [Type: Int32 (0)]    
select @@IDENTITY    
UPDATE Blog SET Version = @p0, Description = @p1 WHERE BlogId = @p2 AND Version = @p3;@p0 = 5 [Type: Int32 (0)], @p1 = ‘Testing blog’ [Type: String (0)], @p2 = 2 [Type: Int32 (0)], @p3 = 4 [Type: Int32 (0)]    
UPDATE Post SET Description = @p0, BlogId = @p1 WHERE PostId = @p2;@p0 = ‘Post 2′ [Type: String (0)], @p1 = NULL [Type: Int32 (0)], @p2 = 12 [Type: Int32 (0)]    

As you can see, the last SQL instruction is trying to set Post table’s foreign key to null and that will not end well. Setting the delete orphan entities attribute does ensure that NH will generate a DELETE instead of an UPDATE. Here’s the SQL generated when you add the delete orphan entities mapping:

SELECT blog0_.BlogId as BlogId0_0_, blog0_.Version as Version0_0_, blog0_.Description as Descript3_0_0_ FROM Blog blog0_ WHERE blog0_.BlogId=@p0;@p0 = 2 [Type: Int32 (0)]    
SELECT posts0_.BlogId as BlogId1_, posts0_.PostId as PostId1_, posts0_.PostId as PostId1_0_, posts0_.Description as Descript2_1_0_, posts0_.BlogId as BlogId1_0_ FROM Post posts0_ WHERE posts0_.BlogId=@p0;@p0 = 2 [Type: Int32 (0)]    
INSERT INTO Post (Description, BlogId) VALUES (@p0, @p1);@p0 = ‘Post 2′ [Type: String (0)], @p1 = 2 [Type: Int32 (0)]    
select @@IDENTITY    
UPDATE Blog SET Version = @p0, Description = @p1 WHERE BlogId = @p2 AND Version = @p3;@p0 = 7 [Type: Int32 (0)], @p1 = ‘Testing blog’ [Type: String (0)], @p2 = 2 [Type: Int32 (0)], @p3 = 6 [Type: Int32 (0)]    
DELETE FROM Post WHERE PostId = @p0;@p0 = 18 [Type: Int32 (0)]    

There’s still more to say about inverse (for instance, we haven’t discussed many-to-many relationships), but I believe that this is more than enough for getting you started.

That’s it for now. Stay tuned for more!

About these ads

~ by Luis Abreu on January 28, 2013.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
Follow

Get every new post delivered to your Inbox.

Join 485 other followers

%d bloggers like this: