NHibernate entity loose coupling
Let's say I have an entity called MyItem. It can be included in many "parents", like SomeCollection and SomeOtherCollection. Because it can be included in many parents, and since I don't want MyItem to know about the parents, I'd like to not have any properties in MyItem referencing a parent.
And since a parent, like SomeCollection, can c开发者_如何学编程ontain many many MyItems, I feel like I need to have some sort of paging involved in getting the children from a parent. This would keep me from having a property in SomeCollection referencing MyItems. Lazy loaded or not, it's always "all or nothing" (right?).
I definitely need some reference between MyItem entities and their parents though, in the form of a mapping table in the database.
Questions:
- How do I create mappings for this? Can I have mappings, or should the relation be kept in the business logic instead?
- How would I query which MyItem entities exist in SomeCollection? Can I do this with only one trip to the database using ICriteria?
Many-to-One
Parent contains a property Child, the child may be linked from several parents.
class Parent
{
public virtual MyItem Child { get; set; }
}
<class name="Parent">
<many-to-one name="Child" column="MyItemId" />
</class>
Many-to-Many with a join table
Parent contains a collection of Children, the children may be linked from several parents.
class Parent
{
public virtual IList<MyItem> Children { get; set; }
}
<class name="Parent">
<bag name="Children" table="parent_myitem">
<key column="parentid" />
<many-to-many class="MyItem" column="MyItemId" />
<bag>
</class>
Criteria Querying
// find Parent with child named "foo".
DetachedCriteria.For<Parent>()
.CreateAlias("Child", "c")
.Add(Restrictions.Eq("c.Name", "foo"));
// find Parent with particular child
DetachedCriteria.For<Parent>()
.Add(Restrictions.Eq("Child", child ));
// find Parent with one of children named "foo".
DetachedCriteria.For<Parent>()
.CreateAlias("Children", "c")
.Add(Restrictions.Eq("c.Name", "foo"));
// find a "page" of children for a parent
DetachedCriteria.For<Parent>()
.Add(Restrictions.Eq("Id", parent.Id ))
.CreateAlias("Children", "c")
.SetFirstResult( 1041 )
.SetMaxResults( 20 )
.GetExecutableCriteria( session )
.List<MyItem>();
That last query may or may not be more efficiently done by using just lazy-loading the whole children collection on first access, and then indexing into it on subsequent "pages". It depends on your data and usage.
Unless I knew a priori that the child collections will be gigantic, I would go the lazy load route first. If timings and profiling show serious slowness, then I would switch to the Criteria method.
I have had a case like yours. I had Configuration class which can be either Global Configuration, Project Specific Configuration, or User Specific Configuration. What I did was this:
- I map the Parent like I normally would in any case to all possible parents (except global, it means applied to all so no parents)
< class name="ConfigurationDomain" table="configuration"> < property name="ProjectId" column="project_id" type="int" insert="false" update="false" /> < property name="UserId" column="user_id" type="int" insert="false" update="false" /> < many-to-one name="Project" column="project_id" lazy="false" /> < many-to-one name="User" column="estimator_id" lazy="false" /> < /class>
- I map the configuration as collection in every possible parents
< class name="UserDomain" table="user"> < set name="ConfigurationList" lazy="true" cascade="all-delete-orphan"> < key column="user_id" /> < one-to-many class="ConfigurationDomain" /> < /set> < /class> < class name="ProjectDomain" table="user"> < set name="ConfigurationList" lazy="true" cascade="all-delete-orphan"> < key column="project_id" /> < one-to-many class="ConfigurationDomain" /> < /set> < /class>
Just like that and it worked for me. How do I access configuration of, say, a user with id 55 is this:
I don't use someUser.ConfigurationList since it's slow. I only map the parent so that I can do this in HQL (it's much faster):
select c from ConfigurationDomain c where c.UserId=55
And to get Global Configuration I would do this:
select c from ConfigurationDomain c where (c.UserId IS NULL) and (c.ProjectId IS NULL)
On reflection, I think you can even remove the collection mapping if you decide to use HQL.
NOTE: I've used Criteria in my early days with NHibernate, but then I found HQL is somewhat more powerful for me, other might have different opinion in this.
精彩评论