How can I do this query with NHibernate - sql

How can I do this query with NHibernate
select top 10 count(distinct classedition.createdby_id) as editions, class.id,
class.name, class.createdon, class.createdby_id
from class
inner join classedition on class.id = classedition.class_id
group by class.id, class.name, class.createdon, class.createdby_id
order by editions desc, class.createdon desc
I'm using NHibernate 3. I tried to do it with the new Linq provider without success. I don't care about the way of doing it as long as it produce the exact sql query above. I would prefer writing a strongly typed query, without magic string if possible.
I'm new to NHibernate so this question may be simple.
Here is a little more info
I use Fluent NHibernate with AutoMappings. The C# classes are very simple:
public class Class
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual DateTime CreatedOn { get; set; }
}
public class ClassEdition
{
public virtual int Id { get; set; }
public virtual Class Class { get; set; }
public virtual User CreatedBy { get; set; }
}
public class User
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}

I finally resolved the problem by using a View.
I don't want to be rude, but it seems like the NHibernate community is more inclined to argue on the way I ask a question then responding to the question itself. See the comments for Diego Mijelshon's answer. I received the same reproaches (about using unit tests) on nhusers (Google Groups): http://groups.google.com/group/nhusers/browse_thread/thread/4c74269aefb918fc

HQL queries are strongly typed and object oriented.
var results = session.CreateQuery(#"
select count(distinct e.CreatedBy), c.Id,
c.Name, c.CreatedOn, c.CreatedBy
from ClassEditions e
join e.Class c
group by c.Id, c.Name, c.CreatedOn, c.CreatedBy
order by 1 desc, c.CreatedOn desc
")
.SetMaxResults(10)
.List();
Your C# code is a string too. NH also has a query compiler.
In fact, if you put the query in a mapping file, you can even get intellisense and real-time error checking by installing the HQL Language Service for Visual Studio.
And a simple unit test that does nothing more than build the SessionFactory will tell you if anything broke because of a change. Not to mention modern refactoring tools (like Resharper) are able to rename identifiers in strings, bindings, or any kind of files without a problem.

Related

Splitting Out a Table to Improve Performance

my user's table in the database is becoming increasingly larger (in terms of columns not rows) and as a consequence is slowing down various areas of my site. This is because it tries to grab every column from the user's table everytime it does a join against it.
I figured i would keep all the common fields in the user's table and then put the additional fields in seperate tables. For example, say i have the following tables in my database:
Users:
- UserID (PK, Identity)
- UserName
- Password
...
UsersActivity:
- UserID (PK, FK)
- LastActivityDate
- LastLoginDate
...
UsersPreferences:
- UserID (PK, FK)
- HtmlEmail
- HideEmail
...
With the following entities:
public class User {
public virtual int UserID { get; set; }
public virtual string UserName { get; set; }
public virtual string Password { get; set; }
public virtual UserActivity Activity { get; set; }
public virtual UserPreferences Preferences { get; set; }
}
public class UserActivity {
public virtual User User { get; set; }
public virtual DateTime LastActivityDate { get; set; }
public virtual DateTime LastLoginDate { get; set; }
}
public class UserPreferences {
public virtual User User { get; set; }
public virtual bool HtmlEmail { get; set; }
public virtual bool HideEmail { get; set; }
}
I was just wondering what is the best way to map this for optimum performance? I figured i could do a one-to-one mapping on the Activity and Performance properties in the User entity. However as far as i understand one-to-one mapping doesn't support lazy loading and this approach would end up being slower.
I also looked into component mapping and wasn't too sure whether i could map this into a seperate table (please correct me if it would be better to keep it in the same table) and whether components supported lazy loading.
Before i go about doing some heavy refactoring of my application i thought i would get the opinion of someone who might have done this. Really appreciate the help.
Thanks
Edit: I found that you could lazy load a one-to-one relationship as long as it is required/constrained. Which it is my case. Therefore i went ahead and carried out the instructions in the following article:
http://brunoreis.com/tech/fluent-nhibernate-hasone-how-implement-one-to-one-relationship/
The trouble now is that i get the error:
NHibernate.Id.IdentifierGenerationException: NHibernate.Id.IdentifierGenerationException: null id generated for: UserActivity.
In NHibernate 3.0 one-to-one relationship supports lazy loading.
And I think that it is better to use Component with combination of Lazy property. Then you will be able to leave all properties in one table and not load them all at once.
You should do some additional application profiling to determine why you're having a performance problem. It's unlikely that it's due to the number of columns in the select list. You probably have an N+1 select problem.
That said, there are many good reasons to use a lightweight object so you might want to look at implementing a DTO (data transfer object) for this.

Problem Using NHibernate Criteria API to Get Specific Results from a Referenced Object

I have an Class that is named Show one of the properties "Country" is a reference to another table.
Show Class
public class Show
{
public virtual int ID { get; set; }
public virtual Country CountryOrigin { get; set; }
public virtual string EnglishName { get; set; }
}
Country Class
public class Country
{
public virtual int ID { get; set; }
public virtual string Name { get; set; }
}
I have it all mapped and working, but now I am wanting to get more specific results. I have used the criteria api to get all the data and sort it, but now I only want to get shows based on country name. Here is what I thought would work, but apprently doesn't.
public IList<Show> AllShowsByCountry(string countryName)
{
IList<Show> shows;
shows = _session.CreateCriteria(typeof(Show))
.Add(Restrictions.Eq("CountryOrigin.Name", "China" ))
.AddOrder(Order.Asc("EnglishName"))
.List<Show>();
return shows;
}
I was thinking that the first part of the restriction might work similar to HQL and you can use objects.
1) The question I guess is am I mis-understanding how HQL works or criteria or both?
2) Also how would you do this properly using criteria?
Update
Here is the error I am getting
could not resolve property: CountryOrigin.Name of: Entities.Show
For Criteria, use the following:
_session.CreateCriteria<Show>()
.CreateAlias("CountryOrigin", "country")
.Add(Restrictions.Eq("country.Name", countryName))
.AddOrder(Order.Asc("EnglishName"))
.List<Show>();
Of course HQL is easier when you are not constructing a dynamic query (search):
_session.CreateQuery(
#"
from Show
where CountryOrigin.Name = :countryName
order by EnglishName
")
.SetParameter("countryName", countryName)
.List<Show>();
And Linq always rocks:
_session.Query<Show>()
.Where(s => s.CountryOrigin.Name = countryName)
.OrderBy(s => EnglishName)
.ToList();
(.Query is for NH 3.x; for 2.x use .Linq)

Selecting objects not in a collection in NHibernate with ICriteria Interface

In my system Users own 0 or more Categories. Here is a simplified version of my model classes:
public class User
{
public virtual String Name {get; set;}
public virtual IList<Category> Categories { get; set; }
}
public class Category
{
public virtual String Title {get; set;}
}
I now want to create an ICriteria query to select all categories that aren't assigned to a user, but I'm stuck. Ideally I don't want to create a navigation property from Category to User, but with my beginner knowledge of NHibernate that's the only solution I can see.
Is there an ICriteria query that will do this with the current data model classes?
Thanks for your help.
This is off the top of my head, but might be a useful pointer.
var crit = _session.CreateCriteria<Category>("c")
.Add(
Subqueries.PropertyNotIn("c.id",
DetachedCriteria.For<User>("u")
.CreateCriteria("Categories","uc")
.SetProjection(Projections.Property("uc.id"))
));
var unassignedCategories = crit.List<Category>();
You can probably get a feel for the SQL that will be generated here:
select c.* from categories where c.id not in (select uc.id from usercategories)
Hope this helps, and sorry I haven't been able to test it :)
Tobin

Cannot use collections with InExpression

I've just delved into a bit of NHibernate and I'm having trouble with one of the more 'complex' (to me!) queries I have to write. The scenario is:
I've got a 'Staff' object which has a collection of 'Skills' attached. I'd like to pass in a list of 'Skills' to query against (e.g. if I only want people that can either 'Cook' or 'Code', or both) and return a list of matching Staff, but I'm having a little trouble....
What I've got object-wise is:
public class StaffMember : Resource
{
public virtual string EmployeeId { get; set; }
public virtual bool IsTeamLeader { get; set; }
public virtual StaffMember TeamLeader { get; set; }
public virtual IList<Skill> Skills { get; set; }
}
public class Skill : BaseDomainObject
{
public virtual string Name { get; set; }
}
And I guess the SQL would go something like:
select distinct st.*
from staff st, resource re
inner join staffskills sks on re.id = sks.staffresourceid
inner join skill ski on ski.id = sks.skillid
where st.resourceid = re.id
and ski.id in (1,2,3,4)
I tried to use "Expression.InG("Skills", skillsSearchList)" in the criteria, but that can't be used when two collections are in play (e.g. if they only had one skill, it would be fine!)... any pointers?
You need to
.CreateAlias("Skills", "sks")
.Add(Restrictions.In("sks.id", skillIdList))
I'm not sure if this can be done with a list of skill objects. Either way, the sql is going to end up the same as above.
Note that this will create a Cartesian product so you might want to use an exists subquery or .SetResultTransformer(new DistinctRootEntityResultTransformer()) to get back a list of distinct Staff.

How do you map aggregate functions in NHibernate?

I'm new to NHibernate and trying to create my first mapping.
I have created a class like this (my example is simplified):
public class Buyer
{
public int BuyerID { get; set; }
public string Name { get; set; }
public decimal AverageOrderAmount { get; private set; }
public DateTime LastOrderDate { get; private set; }
}
Normally, to get this data out of SQL Server, I would write a query using aggregate functions like this:
select b.BuyerID, b.Name,
avg(o.OrderTotal) as AverageOrderAmount, max(o.OrderDate) as LastOrderDate
from Buyers b
join Orders o on o.BuyerID = b.BuyerID
where BuyerID=#BuyerID
group by b.BuyerID, b.Name
My question is, how do I communicate this in my mapping? Is this possible?
I supposed I could store these calculated values in the Buyers cable and recalculate them as needed, but that doesn't feel right.
From what I know, you can't map that using nhibernate. An entity represents a table(most of the times) , what you have there is more along the lines of a report.
To have access to that class I would create a view in the database and then create a separate entity. In your normal use of Buyer you probably don't always need AverageOrderAmount and LastOrderDate - I think that you're using that to display this information on the interface , in which case you should create and map a DB view.