Load collections eagerly in NHibernate using Criteria API - nhibernate

I have an entity A which HasMany entities B and entities C. All entities A, B and C have some references x,y and z which should be loaded eagerly.
I want to read from the database all entities A, and load the collections of B and C eagerly using criteria API.
So far, I am able to fetch the references in 'A' eagerly. But when the collections are loaded, the references within them are lazily loaded.
Here is how I do it
AllEntities_A =
_session.CreateCriteria(typeof(A))
.SetFetchMode("x", FetchMode.Eager)
.SetFetchMode("y", FetchMode.Eager)
.List<A>().AsQueryable();
The mapping of entity A using Fluent is as shown below. _B and _C are private ILists for B & C respectively in A.
Id(c => c.SystemId);
Version(c => c.Version);
References(c => c.x).Cascade.All();
References(c => c.y).Cascade.All();
HasMany<B>(Reveal.Property<A>("_B"))
.AsBag()
.Cascade.AllDeleteOrphan()
.Not.LazyLoad()
.Inverse()
.Cache.ReadWrite().IncludeAll();
HasMany<C>(Reveal.Property<A>("_C"))
.AsBag()
.Cascade.AllDeleteOrphan()
.LazyLoad()
.Inverse()
.Cache.ReadWrite().IncludeAll();
I don't want to make changes to the mapping file, and would like to load the entire entity A eagerly. i.e. I should get a List of A's where there will be List of B's and C's whose reference properties will also be loaded eagerly

You're trying to do a cartesian product here. I think NHibernate requires mapping the relations as sets instead of bags to do that, since bags allow duplicates.
Anyway, cartesian products are very inefficient. Use a multi-query or future queries instead.
See:
Nhibernate: eager loading two child collections (one being a component list)
https://nhibernate.jira.com/browse/NH-1471
http://nhibernate.info/blog/2008/09/06/eager-loading-aggregate-with-many-child-collections.html
http://ayende.com/Blog/archive/2010/01/16/eagerly-loading-entity-associations-efficiently-with-nhibernate.aspx

Related

Yii Model - MANY_MANY relations - do I still need a HAS_MANY?

I am defining the many to many relationship between two objects (ModelA & ModelB for this example) through three tables/active record models in the following way:
ModelA --< ModelA_B >-- ModelB
Where ModelA_B contains a foreign key field to both ModelA and ModelB. So in the code for ModelA I have have added to the relations() function:
'modelbs' => array(self::MANY_MANY, 'ModelB', 'tbl_modelb(modela_id,modelb_id)'),
My question is do I still need the HAS_MANY relationship that was generated by Gii to represent the relation to the linking table ModelA_B or is this declared implicitly by the MANY_MANY above?
'modelabs' => array(self::HAS_MANY, 'ModelA_B', 'ModelA_Id'),
If you use a MANY_MANY relation, you don't need to define another HAS_MANY relation for the ModelA_B table.
But you could also use the through feature, which will replace the MANY_MANY relation at some point (probably in Yii 2.0 if i remember right). In this case you would define 2 relations:
'mobelabs' => array(self::HAS_MANY, 'ModelA_B', 'ModelA_Id'),
'modelbs' => array(self::HAS_MANY, 'Model_B', 'ModelB_Id', 'through'=>'modelabs'),
Now you have access to both related records: the ModelA_B via $modelA->modelabs and the ModelB via the $modelA->modelbs.

Fluent NHibernate - How do I create a one to many mapping which has a bridge table in the middle?

How do I create a one to many mapping which has a bridge table in the middle?
I basically have 3 tables: Items, Tags, and TagsToItems.
Each Item can have many Tags as defined by the TagsToItems table. How do I set up this mapping correctly using Fluent NHibernate?
I've been playing with HasMany but haven't quite figured out how this works with a bridge table.
HasMany(x => x.Tags).Table("TagsToItems").KeyColumn("ItemId");
My latest attempt to solve this problem looks like this:
HasManyToMany(x => x.Tags)
.AsBag()
.Table("TagsToItems")
.ParentKeyColumn("ItemId")
.ChildKeyColumn("TagId")
.Cascade.All()
.Inverse();
However this is throwing the error:
Initializing[Namespace.Item#11]-failed to lazily initialize a
collection of role:
Namespace.DataAccess.NHibernate.Entities.Item.Tags, no session or
session was closed
It turns out that the problem is with using the Tags collection associated to an Item.
The Tags collection could not be lazily initialised because by the time I was trying to use it (in my view) the session scope of the NHibernate session had closed.
I solved this by setting .Not.LazyLoad() on the mapping:
HasManyToMany(x => x.Tags)
.AsBag()
.Table("TagsToItems")
.ParentKeyColumn("ItemId")
.ChildKeyColumn("TagId")
.Not.LazyLoad()
.Cascade.All();

How to Delete in a many to many relationship?

I have a many to many relationship:
Product has many Categories and Category has Many Products.
Say I have
Shopping Category
Food Category
Product A - Shopping Category, Food Category
Product B - Shopping Category
Now I delete Shopping Category. I want the Product A reference to be removed from Shopping Category and I want Product B to be removed completely.
I would end up with:
Product A - Food Category.
How do I do this in nhibernate (I am using fluent nhibernate).
I tried to use Cascade DeleteOrphan and AllDeleteOrphan but when I do that and delete Shopping both Product A and B get deleted.
public class CategoryMapping : ClassMap<Category>
{
public CategoryMapping()
{
Id(x => x.Id).GeneratedBy.GuidComb();
Map(x => x.Name).Not.Nullable().NvarcharWithMaxSize();
HasManyToMany(x => x.Products).Cascade.DeleteOrphan();
}
}
public class ProductMapping : ClassMap<Product>
{
public ProductMapping()
{
Id(x => x.Id).GeneratedBy.GuidComb();
Map(x => x.Name).Not.Nullable().NvarcharWithMaxSize();
HasManyToMany(x => x.Categories);
}
}
unitOfWork.BeginTransaction();
Category category =session.Load<Category>(id);
session.Delete(category);
unitOfWork.Commit();
I don't think this can be handled by mapping with existing data structure. I think you would need to write some manual code (*) or change data structure.
(*) Not 100% sure it works though...
unitOfWork.BeginTransaction();
Category category =session.Load<Category>(id);
var productsDel = category.Products.Where(p => p.Categories.Count == 1);
productsDel.ForEach(p => session.Delete(p));
session.Delete(category);
unitOfWork.Commit();
Other:
I'm also thinking about adding mapping for your cross-ref tables. Then you should be able to configure mapping so it will delete only records from that cross-ref table. You will need to verify if there are products without references and delete them periodically. (some periodic clean-up code, like running some stored procedure). I know this solutions smells bad :) There are still triggers and other SQL Server stuff... not good solutions anyway, but solutions.
If you just want to remove the association between the two use Cascade.SaveUpdate()
Then just remove the entity from the collection and commit the transaction if you are using transactions if not you will need to do a Session.Flush

NHibernate Issuing Unwanted Orphaning Statements

Imagine that I have a Parent/Child relationship managed by NHibernate.
I'm receiving a Parent object from an MVC postback that edits its properties; I want to save just the parent to the database without having to load the children from the database.
At the time of the save, Parent has a Children property that is null (because it hasn't been loaded; there are, however, valid Children in the database for that parent).
When I save a modified Parent (ID=100), NHibernate is issuing a "SET Child.ParentId = NULL WHERE Child.ParentId = 100" statement. I don't want this to happen because there could be valid children. I shouldn't have to load them from the database before the save just to prevent them from being orphaned.
The fluent mappings look like this (true entity names genericized for this post):
public ParentMapping()
{
Table("Parent");
Id(x => x.Id).Column("Id").GeneratedBy.Identity();
Map(x => x.ParentProperty1).Column("ParentProperty1").Not.Nullable();
HasMany(x => x.Children).Cascade.None();
}
public ChildMapping()
{
Table("Children");
Id(x => x.Id).Column("Id").GeneratedBy.Identity();
Map(x => x.ChildProperty1).Column("ChildProperty1").Not.Nullable();
References(x => x.Parent).Column("Parent_Id").Not.Nullable().Fetch.Select();
}
To summarize, I want to save an updated Parent instance that was retrieved from an earlier ISession (and passed to a browser and back through MVC model minding); its Children property is null, but in reality it's got plenty of children in the database. I don't want NHibernate to issue any modifying statements to the Children table at all.
I've experimented with Cascade.None() and LazyLoad() in the hopes that this nudges NHibernate to behave differently, but no luck.
Any insight would be appreciated. Thanks!
Jeff
You must specify Inverse() on the has many to tell nhibernate not to worry about this side of the collection

NHibernate cascade deletes

I have a one-to-many relationship in data model from A to B. But in my domain API I do not expose "B"s on A (since we will never navigate from from A to B), but have a reference from B to A. Now I want to be able to delete all "B"s when A is deleted. Is it possible? Right now NH is trying first to set FK to null, which I don't want, and cannot since column is not nullable.
A = SupplierType
B = BaseProductCoInsurance
public BaseProductCoInsuranceMap()
{
Table("BaseProductCoInsurance");
Id(x => x.Id, "BaseProductCoInsuranceId");
Map(x => x.CoInsurancePercent).Column("CoInsrPrcnt");
References(x => x.BaseProduct, "BaseProductId");
References(x => x.PolicySupplierType, "PlcySupplierTypeID");
References(x => x.InsuredType, "InsuredTypeCode");
}
If you need to be able to cascade delete then you need to let NHibernate know about the relationship. That said you don't need to make the relationship accessible to others. You could of course have a private collection that only NH knows about.
If you make the relationship lazy loaded you shouldn't even see a performance hit from this.
Another option to consider is to just modify your delete method to also delete the other entity.