I am using FluentNHibernate and have two classes that have a 1:1 relationship. ItemA includes
public virtual ItemB { get; set; }
and mapped with
HasOne(x => x.ItemB).ForeignKey("ItemID");
I am only interested in ItemA, but because it has the following map I'm seeing two database calls when I run:
var res = session.Query<ItemA>()
.SingleOrDefault(x => x.ItemId.ToString() =="AC3E30FF-E767-440F-AB1F-0000293C5A0C");
I get a select for ItemA table and a select for ItemB table.
If I then make a change back to References, I get only one:
References(x => x.ItemB).ForeignKey("ItemId");
This is perhaps trivial, but I have another class which has been given a number of "HasOne" relationships for tables I do not need to be using and yet Select queries are becoming rather complex.
I tried adding "Lazyload" to the mapping, but that didn't seem to change anything. Is there any way of keeping the HasOne relationship but being able to generate a simple, single "select" query for my "Query" (i.e. Select [columns] from ItemA where ItemID =...)?
No, there is no way how to achieve that. One-to-one will simply be eager, no lazy setting will help.
But as stated here: 5.1.11. one-to-one
A one-to-one association ...
... Alternatively, a foreign key with a unique constraint, from Employee to Person, may be expressed as:
<many-to-one name="Person" class="Person" column="PERSON_ID" unique="true"/>`
// which is in fluent References()
And this association may be made bidirectional by adding the following to the Person mapping:
<one-to-one name="Employee" class="Employee" property-ref="Person"/>
So, I would suggest go with References mapping
References(x => x.ItemB).Unique().Cascade.All();
See: How to do a fluent nhibernate one to one mapping?
Related
I'm trying to get a many to many relationship to work using Fluent Nhibernate.
I have a Product and a RelatedProduct Table
Product {Id, Name...}
ProductRelated{Id, ProductId, RelatedProductId, relOrder}
and a Product class
The mapping looks like
HasManyToMany(x => x.RelatedProducts)
.Table("ProductRelated")
.ReadOnly()
.ChildOrderBy("relOrder asc")
.ParentKeyColumn("ProductId")
.ChildKeyColumn("RelatedProductId");
When a query is done for Product and the RelatedProducts are lazy loaded I can see that the sorting is applied correctly using the relOrder on the join table.
Session.Query<Product>()
.FetchMany(p => p.Categories)
.FetchMany(p => p.Departments)
Once I add in eager loading of the related products NHibernate tries to sort by a relOrder column on the product itself instead of on the join table.
Session.Query<Product>()
.FetchMany(p => p.Categories)
.FetchMany(p => p.Departments)
.FetchMany(p => p.RelatedProducts)
Any ideas of whats going on here?
Well to answer your question what's going on here?, I would say, you are using: "not-together fitting features" of NHibernate.
A snippet from documentation 6.6. Sorted Collections:
Setting the order-by attribute tells NHibernate ...
Note: that lookup operations on these collections are very slow if they contain more than a few elements.
Note: that the value of the order-by attribute is an SQL ordering, not a HQL ordering!
So, this could be applied only for "standard" lazy loading, becuase this kind of a feature is applied only on a DB side. It is not managing order in the memory.
And the eager fetching, as the counter-part, is a different way how to generate and issue the SQL Statement to DB.
So, eager and order-by will never work together.
*
My NOTE: I simply have to append this. I can't help myself
I.
The Eager fetching is the feature which should be avoided (I never use it, but it's me). There is a better solution and it is setting the BatchSize(), which will reduce the 1+N into 1+(a few) and will keep all the (lazy) featrues, including order-by. Check these if interested:
NHibernate QueryOver with Fetch resulting multiple sql queries and db hits
Is this the right way to eager load child collections in NHibernate
https://stackoverflow.com/questions/18419988/
BatchSize() is supported for HasManyToMany as well: ToManyBase:
/// <summary>Specify the select batch size </summary>
/// <param name="size">Batch size</param>
public T BatchSize(int size) { ...
II.
The many-to-many mapping, while fancy at first look, is not the way I'd suggest. Try to rethink your model and introduce the first-level-citizen: PairingEntity - for the pairing object. It will then use many-to-one and one-to-many mapping which could give us more... e.g. improved querying like Subqueries... try to check these:
How to create NHibernate HasManyToMany relation
many-to-many with extra columns nhibernate
Nhibernate: How to represent Many-To-Many relationships with One-to-Many relationships?
I have a [User] table/class and a [Company] table/class and there is a link-table [UserCompany] between them.
When editing a User, beside basic information people also could change that user's access Companies, so I do the map like this in UserMap.cs:
HasManyToMany(u => u
.Companies)
.Cascade.SaveUpdate()
.Table("UserCompany")
.ParentKeyColumn("UserId")
.ChildKeyColumn("CompanyCode")
.Not.LazyLoad();
Also in CompanyMap.cs I set inverse like this:
HasManyToMany(c => c.Users)
.Inverse()
.Table("UserCompany")
.ParentKeyColumn("CompanyCode")
.ChildKeyColumn("UserId");
The problem now is: I could update [User] information/table, plus the linking data in [UserCompany] table. However, the Fluent Nhibernate also update the [Company] table which I don't need at all.
Is there any way I could let FN not update Company table?
To stop cascading updates just remove the
.Cascade.SaveUpdate()
from your Many-to-many mapping.
It could be a bit confusing. In comparison with the cascading used on <list>s and <map>s. In that case, the update is done directly on the child table (parent has more children... child contains the ParentId - cascade is reasonable to do operation on the child record)
But here we are working with a pair table. The relation is stored there. And this table will be always managed by NHibernate (implicit cascading).
The setting .Cascade.SaveUpdate() goes to other end of the many-to-many relation. To company table in our case. It could be handy.. but you can omit that and get everything running as expected.
I am using fluent nHibernate for my mappings as follow:
public class ContentTagMap: ClassMap<Employee>
{
public EmployeeMap()
{
Id(t => t.Id);
Map(t => t.Name);
HasManyToMany(t => t.Company);
}
}
public class CompanyMap: ClassMap<Company>
{
public HelpMap()
{
Id(h => h.Id);
Map(h => h.CompanyName).Length(6000);
Map(h => h.address).Length(6000);
HasManyToMany(h => h.Employee);
}
}
These mappings produce Employee Table ,Company Table and EmployeeToCompany Table
Employee Table
Id Name
1 John
2 MAX
Company Table
Id CompanyName address
1 HTC ABC
2 HTC2 India
EmployeeToCompany Table
Employee_Id Company_Id
1 1
2 1
How can I delete the employee with Id 1?
Unless I'm misunderstanding your question you should be asking:
How can i delete the content of the table using NHibernate?
Fluent NHibernate is only a tool to aid in the mapping of your entities. NHibernate is what you use to create, read, update and delete data. In any event:
9.5. Deleting persistent objects
ISession.Delete() will remove an object's state from the database. Of
course, your application might still hold a reference to it. So it's
best to think of Delete() as making a persistent instance transient.
From the NHibernate Documentation
You probably want to also define a Cascading style on your many to many relationship (HasManyToMany) in your mapping.
If you use Cascade.SaveUpdate in your many to many whenever you delete an entity on one side of the relationship it will delete that entity and any relationships. If you remove the association (ex. if you removed an Employee from a Company) it will only delete the relationship (EmployeeToCompany). This is what I've typically found to work in the case of many to many relationships.
Look at this article for more details on mapping and using a many to many relationship in Fluent NHibernate.
how to keep many to many relationships in sync with nhibernate?
I have a mapping in FluentNHibernate for a HasMany relationship and I'd like to specify a Table on it to override the default Table that nHibernate will look in to find those objects that I have many of. Does that make sense?
So lets say I have a table for Invoices and a table for InvoiceItems and lets say I have table called InvoiceItemsTwo.
I have a class for Invoice and a Class for InvoiceItems as well, and their mappings are pretty straight forward. I'd like to specify in my mapping for Invoice, that it should look for it's items in InvoiceItemsTwo instead of the default InvoiceItems.
So my mapping of that relationship looks like this
HasMany(c => c.InvoiceItems).Cascade.SaveUpdate().Table("InvoiceItemsTwo");
But this doesn't work. I keep getting an error from my website at runtime that says Invalid object name 'InvoiceItems'.
Why is it ignoring the fact that I am explicitly specifying the Table in my mapping on the relationship?
I tried dumping the mapping at run time and it's being setup something like this
<bag cascade="save-update" table="InvoiceItemsTwo">
Any ideas?
The table attribute applies only to many-to-many relationships, not one-to-many.
you can't specify a different table in your mapping class. Fluent NHibernate uses the class mapped on the property list (InvoiceItems).
If yoy want to use another class to map your details table you must create a InvoceItemsTwo class and map it in your master table class.
You could map the list as composite-element instead of a one-to-many relation and then map it to another table. But it is not a good idea. Consider that NH needs to know where to store an object which is in memory. So it may happen that the object is stored in the wrong table.
Either store all the InvoiceItems in separate tables using composite-element instead of one-to-many and components instead of many-to-one (however this is called in Fluent).
Or store all the InvoiceItems in the same table and use regular references.
I had a following structure:
User has many books in his history
which was translated to the following
class User { ICollection<Book> History } // C#
User -> UserHistory (UserId, BookId) -> Book // DB
Now I want to add a date to the history, creating the following class structure:
class User { ICollection<Read> History }
class Read { Book Book, Date At }
and keeping db schema almost unchanged
User -> UserHistory (UserId, BookId, At) -> Book
I want to map Read to UserHistory, the questions are:
What should I use as id in mapping of Read? UserHistory primary key is (UserId, BookId). Do I need an id for NH to work?
UserHistory -> Book seems to be a case of one-to-one.
How to specify the BookId column name in UserHistory in this case?
I do not see a column attribute on one-to-one (and there is a reason for me to be explicit about column name).
In your first scenario, the UserHistory was simply a mapping table for a many-to-many relationship and didn't have a proper object. Now, the UserHistory table/Read class is a separate entity so it will need an identifier of some sort. The easiest way is to add a primary key ReadId (or UserHistoryId) to the UserHistory table.
You don't have to add a separate key and could use a composite key on UserId and BookId -- but can a user read a book more than once? Assuming so, then you would also need to add the At column to the composite key to make it unique. This gets ugly and will lead to other issues when dealing with collections, so it isn't worth it.
The UserHistory to Book relationship is actually a many-to-one rather than a one-to-one. Many different users can read the same book (and perhaps the same user can read a book more than once). Think of many-to-one as an object reference.
Question 1: no, you don't need an id, you just can map it as component (or composite-element in this case, where it is in a list)
Question 2: User-history -> book is not one-to-one, many users could have read the same book over time, so it's many-to-one and should be mapped so.
Probably incomplete mapping looks as following:
<class name="User">
<bag name="History" table="UserHistory">
<key name="UserId">
<composite-element class="Read">
<property name="At" />
<many-to-one name="Book" column="BookId" />
</composite-element>
</bag>
Note: Forget about one-to-one mappings. This is used very very rarely, when you have two tables which share the same primary key and are this way linked together really one-to-one. In most cases, you need many-to-one, even if in the real world it is actually one-to-one.