Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NHibernate mapping of lookup collection (if that makes any sense)

Tags:

c#

nhibernate

I'm not sure how to ask the question, for I don't know what I don't know, and therefore I don't know the proper terminology for what I'm trying to get the answer to. I will explain my scenario, in hopes that it will help:

I've got three tables, a Book table, a Tag table and a BookTag lookup table.

Each book has an ID, a Title (for starters) Each tag has an ID, and a Title Each BookTag has an ID, a BookID, and a TagID.

A book can be tagged with multiple tags, and a tag can be used on more than one BookID.

I've got my objects setup in this fashion:

Book.cs
int BookID
string Title
List<BookTag> Tags

Tag.cs
int TagID
string Title

BookTag.cs
int ID
int BookID
int TagID

I would like the Books.cs class to have a collection of Tags, and not BookTags, but I cannot seem to get the mapping right in NHibernate. This is what I've got for the Book.hbm.xml file:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DomainModel" namespace="DomainModel.Books">
  <class name="DomainModel.Books.Book" table="Books">
    <id name="BookID" type="Int32" unsaved-value="0">
      <generator class="native"/>
    </id>
    <property name="Title" type="String" not-null="true"/>
    <set lazy="true" name="Tags" table="BookTags" generic="true" inverse="true" cascade="delete">
      <key column="BookID"/>
      <one-to-many class="DomainModel.Books.BookTag, DomainModel"/>
    </set>
  </class>
</hibernate-mapping>

And this is my BookTag.hbm.xml:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DomainModel" namespace="DomainModel.Books">
  <class name="DomainModel.Books.BookTag" table="BookTags">
    <id column="BookTagID" name="BookTagID" type="Int32" unsaved-value="0">
      <generator class="native"/>
    </id>
    <many-to-one name="Tag">
      <column not-null="true" name="TagID"/>
    </many-to-one>
    <many-to-one name="Book">
      <column not-null="true" name="BookID"/>
    </many-to-one>
  </class>
</hibernate-mapping>

Under this model, I can get to the tag I want by using my object model: Book.Tags[0].Tag, but that just seems inefficient. Can I use NHibernate to map out the BookTags.TagID with the Tags.TagID in the database so that I can get Book.Tags[0] to return a Tag object, instead of a BookTags object? I didn't know of a better way to associate Books to tags so that a tag used on Book1 can be used on Book2 without adding a new entry to the Tags table.

I hope this makes at least some sense. Let me know if you need further clarification. I'll post my solution here if I figure it out before someone answers.

like image 644
Carl Avatar asked Nov 25 '08 22:11

Carl


2 Answers

You don't need a BookTag class at all. You can map Book.Tags collection as many-to-many. To do this you will specify BookTag in the map to connect the association. Look here in section 6.8 Bidirectional Associations.

like image 86
Tim Scott Avatar answered Oct 03 '22 06:10

Tim Scott


Thank you Tim, that was what I needed. For those that are curious, I "un-mapped" the BookTag table/objects, and now just have a Book object and a Tag object that are used and mapped to NHibernate.

My Book.hbm.xml was updated to this:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DomainModel" namespace="DomainModel.Books">
  <class name="DomainModel.Books.Book" table="Books">
    <id name="BookID" type="Int32" unsaved-value="0">
      <generator class="native"/>
    </id>
    <property name="Title" type="String" not-null="true"/>
    <bag name="Tags" table="BookTag" generic="true">
      <key column="BookID" on-delete="noaction"></key>
      <many-to-many class="Tag" column="TagID"></many-to-many>
    </bag>
  </class>
</hibernate-mapping>
like image 42
Carl Avatar answered Oct 03 '22 08:10

Carl