I have a recursive one-to-many relationship that has the default lazy value of true. What code can I write against the NH API that will efficiently retrieve the ENTIRE tree AS IF I had lazy="false" on the SubCategories mapping?
Here's the recursive one-to-many relationship:
<class name="Category" lazy="false">
...
<list name="SubCategories" fetch="subselect">
<key column="ParentCategoryID"/>
<index column="PositionInList"/>
<one-to-many class="Category"/>
</list>
I don't specify lazy="false" on the list since laziness is required in about half the queries I need to run. I have fetch="subselect" on the list as an optimization for when I do manage to retrieve the entire tree.
I've tried the ICriteria API:
session.CreateCriteria<Category>().SetFetchMode( "SubCategories", FetchMode.Eager ).Add( Restrictions.IsNull("ParentCategory") ).SetResultTransformer( CriteriaSpecification.DistinctRootEntity ).List<Category>();
but that only eagerly loaded only the first level in the hierarchy.
See Ayende's site: Efficiently Selecting a Tree. I have successfully used this technique in my own applications. With ICriteria, it looks like this:
session.CreateCriteria<Category>()
.SetFetchMode("SubCategories", FetchMode.Join)
.SetResultTransformer(new DistinctRootEntityResultTransformer())
.List<Category>()
.Where(x => x.ParentCategory == null);
The main difference between this version and what you tried is how the "ParentCategory == null" filter is applied. It has to be left out of the query that is sent to the database in order to retrieve the whole tree - but we still need the query to only return the root nodes of the tree, so we'll use linq to find those after the database query has completed.
I used Daniel's code as a bases for solving the problem. I also experimented with the equivalent HQL that I shared below. The HQL executed slightly faster, but I went with ICriteria since I could then choose between FetchModel.Join and FetchModel.Lazy.
session.CreateQuery( "from Category as c left join fetch c.SubCategories" )
.SetResultTransformer( new DistinctRootEntityResultTransformer() )
.List<Category>()
.Where( c => c.ParentCategory == null );
Not sure if it helps but take a look at : map a tree in NHibernate
Related
I Have 2 classes of the same table.
one class, "Entity", contains properties x,y,z from table ENTITY
and the derived class, "ExtendedEntity" contains some more properties (collections - relations to other tables).
I want to map both of them but i couldn't find a way to map subclasses without using discriminator (I dont need one, sometimes i want to select the Entity object and sometimes the ExtendedEntity).
does anyone has any idea?
(I tried to map the ExtendedEntity by copying the Entity mapping and adding the new properties, but now when i want to get the Entity object it brings me ExtendedEntity).
Thanks!
Taking into account the first sentence of your question:
I Have 2 classes of the same table.
It means that there is only one table ("ENTITY"). If this is true, there do not have to be, and even should not be any inheritance, if no discriminator exists. If both entities will be related to all rows, then (instead of inheritance) we need laziness. And only one Entity profiting from the native NHibernate behavior: having lazy properties. And even more, to optimize some queries - the Projections could be used.
But again, my suggestion could be correct only if I do understand your scenario correctly: the two entities were introduced only to reduce workload; but both target all rows in one table.
The mapping for the only class
<class name="Entity" table="ENTITY" lazy="true">
<id name="ID" column="Entityd">
<generator class="native"></generator>
</id>
<!-- standard default always loaded properties -->
<property name="Code" />
<property name="Name" />
<!-- only if accessed, lazily loaded properties -->
<many-to-one lazy="proxy" name="Currency" column="CurrencyId" cascade="none" />
<property lazy="true" name="Image" column="ImageBinary" />
...
Having this mapping we can get first 5 Entities
var list = session.CreateCriteria<Entity>()
.SetMaxResults(5)
.List<Entity>();
And SQL statement generated will be:
SELECT TOP 5 this_.EntityId as EntityId3_1_
, this_.Code as Code3_1_
, this_.Name as Name3_1_
FROM ENTITY this_
And we can even reduce it with projections
var propertyList = NHibernate.Criterion.Projections.ProjectionList();
// projection properties
propertyList.Add(NHibernate.Criterion.Projections.Property("ID"));
propertyList.Add(NHibernate.Criterion.Projections.Property("Code"));
var projected = session.CreateCriteria<Entity>()
.SetMaxResults(5)
.SetProjection(propertyList)
.SetResultTransformer(new NHibernate.Transform
.AliasToBeanResultTransformer(typeof(Entity)))
.List<Entity>();
In this case is the SQL Select even smaller. If the ID and Code would be enough..
SELECT TOP 5 this_.Code as y0_
, this_.EntityId as y1_
FROM ENTITY this_
So, in case I read your question correctly, in your scenario solution would not be in inheritance but in NHibernate native laziness
NOTE: there could be ExtendedEntity derived from Entity even in this scenario. But not for inheritance via NHibernate mapping but for the Projections Transformation. Some properties from many-to-one properties could be projected this way...
I'm new to Nhibernate. My problem is that I want to narrow down a query by using a column that is not included in my entity (ie hbm). I want to do something like this:
Session.QueryOver<MyEntity>()
.SQL_Where("MyFlag = 1")
Since I have no use of that flag later I don't want to include it to the entity
I know I can use:
Session
.CreateSQLQuery("SELECT A,B,C FROM ENTITY WHERE MyFlag = 1")
.SetResultTransformer(Transformers.AliasToBean<MyEntity>())
.List<MyEntity>();
It would be nice to use QueryOver<>(), it's more safe if a column is added etc.
You may be able to use filters:-
Put a filter on your mappings class definition, however this will affect ALL returned rows
e.g.
<class name="Domain.Model.MyEntity, Domain.Model" table="MyTable"
where="(MyFlag=1)">
...
</class>
or it may be possible to use conditional filters with QueryOver
<filter-def name="SetMyFlag">
<filter-param name=":flag" type="System.Int"/>
</filter-def>
<class name="Domain.Model.MyEntity, Domain.Model" table="MyTable">
...
<filter name="SetMyFlag" condition="(MyFlag=:flag)"/>
</class>
and use:-
session.EnableFilter("SetMyFlag").SetParameter("flag", 1);
session.QueryOver<MyEntity>();
Although I have never use conditional filters with unmapped columns so this may not work!
I have an object model where a Calendar object has an IDictionary<MembershipUser, Perms> called UserPermissions, where MembershipUser is an object, and Perms is a simple enumeration. This is in the mapping file for Calendar as
<map name="UserPermissions" table="CalendarUserPermissions" lazy="true" cascade="all">
<key column="CalendarID"/>
<index-many-to-many class="MembershipUser" column="UserGUID" />
<element column="Permissions" type="CalendarPermission" not-null="true" />
</map>
Now I want to execute a query to find all calendars for which a given user has some permission defined. The permission is irrelevant; I just want a list of the calendars where a given user is present as a key in the UserPermissions dictionary. I have the username property, not a MembershipUser object. How do I build that using QBC (or HQL)? Here's what I've tried:
ISession session = SessionManager.CurrentSession;
ICriteria calCrit = session.CreateCriteria<Calendar>();
ICriteria userCrit = calCrit.CreateCriteria("UserPermissions.indices");
userCrit.Add(Expression.Eq("Username", username));
return calCrit.List<Calendar>();
This constructed invalid SQL -- the WHERE clause contained WHERE membership1_.Username = #p0 as expected, but the FROM clause didn't include the MemberhipUsers table.
Also, I really had to struggle to learn about the .indices notation. I found it by digging through the NHibernate source code, and saw that there's also .elements and some other dotted notations. Where's a reference to the allowed syntax of an association path? I feel like what's above is very close, and just missing something simple.
Just trying to do this myself and it looks like this can be done with HQL but not the Criteria API.
https://nhibernate.jira.com/browse/NH-1795
To do it in HQL:
http://ayende.com/Blog/archive/2009/06/03/nhibernate-mapping-ndash-ltmapgt.aspx
Specifically look for Ayende's comment:
It is something like:
select 1 from Profile p join p.Entries e
where index(e) = 'HasCats' and e = 'true'
So I have 2 entities:
Article
Category
And I have a table that relates the articleID and categoryID:
articles_categories
-articleID
-categoryID
I am using xml for my mappings, what should I do here?
I want to be able to query for all articles in a given category.
Use many to many mapping:
<class name="Article">
<set name="Categories" table="articles_categories">
<key column="ArticleId" />
<many-to-many column="CategoryId" class="Category" />
</set>
</class>
You should be able to query like a normal collection.
You can approach this in two ways, depending on how you want to do it. You can set your Category class to have a Collection of Articles. Then to get all of the articles in the category you simply load the Category by id and then call getArticles().
Alternatively, you give the Article a collection of Categories that it belongs to. It all depends on your domain model. Can an article be in multiple categories?
Once you've decided that, take a look at the Hibernate documentation on mapping Collections: http://docs.jboss.org/hibernate/stable/core/reference/en/html_single/#collections
Then your query would look something like the following:
select a from Article a join a.categories c where c.categoryID = :yourCategoryId
If I have a mapping like this:
<class name="Library" table="Libraries">
...
<dynamic-component name="Annotations">
<property name="LibraryResolver.AlgorithmVersion" column="`LibraryResolver.AlgorithmVersion`" type="Int32" />
</dynamic-component>
</class>
How should I write HQL or Linq-to-NHibernate query for all libraries where LibraryResolver.AlgorithmVersion is greater than a given value?
The HQL query below maybe along the lines you are looking for
from Library as lib
where lib.Annotations.LibraryResolver.AlgorithmVersion > 2
If you're using nhibernate, have you tried out the NHibernate LambdaExtensions? This library provides a set of extensions methods over the Criteria and DetachedCriteria apis which removes the need of magic strings when querying using the above two api.
Below is an example of how one might use NHibernate Detached Criteria query with the mentioned LambdaExtensions library
Answer answerAlias = null;
var actual = DetachedCriteria.For<Survey>()
.Add<Survey>( s => s.Status == SurveyStatus.Complete )
.Add<Questionnaire>( q => q.Id == questionnaireId )
.CreateAlias<Survey>( s => s.Answers, () => answerAlias )
.SetProjection( LambdaProjection.Property( () => answerAlias.Id ) );
I don't know whether this helps but when I use the Criteria API (in Java) it just works. Haven't tried with HQL though.
<dynamic-component name="values">
<property name="dynamicNameValue" column="ATTRIBUTE_1" type="string"/>
<property name="dynamicNumber" column="ATTRIBUTE_4" type="integer"/>
</dynamic-component>
Criteria criteria = session.createCriteria(DynamicAttributes.class)
.add(Expression.eq("values.dynamicNumber", 2));
Just some thought: could it be that the problem is that the name ('LibraryResolver.AlgorithmVersion') you're passing contains a dot? Maybe post some code you already attempted?