NHibernate count of items in xml mapping - nhibernate

Is there a way in NHibernate to map the result of a count query to a property on a class? I'd like to do this in the XML mapping.
I know I could formulate this via code (either with a construct that actually queries the count, or by cheating, doing the full query, and counting the resulting items), but it would be nice if I could write some short SQL or HQL and jam that into my XML mapping, somehow.
A concrete example. My DB has these tables -
Entry
Id
BodySummary
Comment
Id
EntryId
Body
I want to get a summary of entries. For each of the entries, I want to get the comment count (and body summary).
FYI: I've omitted irrelevant parts of my DB, like authors, entry title/body, timestamps, etc. This of course should have no bearing on the part of the query I am asking about.

I don't think you can do it with HQL. But here's how to do it with SQL:
<class name="Entry" .... >
<id>
//ID Strategy
</id>
<property name="BodySummary" />
...
<property name="CommentCount" formula="(SELECT COUNT(*) FROM Comment c WHERE c.EntryId = Id)" type="Int32" />
</class>
Important to note:
Wrap the sql in parenthesis - it will error if it is not wrapped
This is not HQL - you have to use the database column/table names not your mapped classes/properties
Provide a return type so NHibernate knows how to map it back to the property
You will probably want to make this a readonly field but this is the basics of how you would map it.
The resultant SQL would be something like this:
SELECT this_.Id as Id11_0_,
this_.BodySummary as BodySummary10_11_0_,
(SELECT COUNT(* )
FROM Comment c
WHERE c.EntryId = this_.Id) as formula0_0_
FROM Entry this_

Related

How to run NHibernate run DB function for each member in a list

I have a DB-intensive function defined in MS SQL that computes a read-only property (LastCompletedDate) of an Inspection object. I don't usually need this information, so I'm not mapping it in Inspection.hbm.xml.
When I do need the info, I want to take an IEnumerable collection of Inspections, query the database to find their LastCompletedDate, and fill that in for each. Ideally, without making a separate trip to the database for each Inspection. I'm having trouble finding a way to do this in NHibernate (I'm a relative newbie to NHibernate). I'm thinking of something like:
CurrentSession.CreateQuery(
"select InspectionId, dbo.fn_GetLastCompletedDate(InspectionId)
from Inspection where InspectionId in :idList")
.SetParameter("idList", from InspectionList select InspectionId)
.List();
followed by a loop to pull out the dates and add them to the Inspection objects.
Is there a better way to do this? And what syntax do I need?
There are two possible options which I can think of.
Mark the property as lazy load
<property name="LastCompletedDate"
lazy="true"
formula="dbo.fn_GetLastCompletedDate(InspectionId)"/>
When performing a query to get all Inspection objects this property will not be loaded.
CurrentSession.CreateQuery("from Inspection")
.List<Inspection>();
But when including a hint this property will be loaded along with all other properties.
CurrentSession.CreateQuery("from Inspection fetch all properties")
.List<Inspection>();
The disadvantage of this approach is that this hint is only available when using HQL. Further details can be found here
http://ayende.com/blog/4377/nhibernate-new-feature-lazy-properties
The second option is to use a component with lazy load enabled.
<component name="lazy_load_items" lazy="true">
<property name="LastCompletedDate"
formula="dbo.fn_GetLastCompletedDate(InspectionId)"/>
</component>
Again this is lazy loaded so a normal query against the Inspection entity wouldn't result in the function being called for each row
CurrentSession.QueryOver<Inspection>.List();
But it can be eager loaded via any of the query APIs
session.QueryOver<Inspection>()
.Fetch(i => i.lazy_load_items).Eager
.List();
This disadvantage of this approach is that is requires an extra class to be created just to contain your single property.
UPDATE
On further testing it looks like eager loading of components only works with HQL using the fetch all properties hint. So the query example I gave is wrong and therefore so are the advantages for the component approach.

ColdFusion ORM Relationship

Before ORM, if I wanted to display the combined output of a normalized table, I’d just do a quick CFQUERY, join the tables on the fields I want and display the output. I just can’t get my head wrapped around it using ORM.
For example with these two tables:
customers
(id,
name,
customerType)
customerTypes
(id,
Name)
How would you create a single entity you can load to display the following when the customerType field in customers links to an id in customerTypes?
customers.id, customers.name, customerTypes.name
All of the ORM relationship examples I’ve walked through for some reason can’t make me understand how to do it. It seems so simple it’s killing me. Any help shedding some light on this would be appreciated!
Or alternatively
<cfproperty name="type" type="string" column="Name" table="customerTypes" joincolumn="id">
see Join mapping in a CFC
So in your Customers CFC you will need something like this:
<cfproperty name="customerType" type="CustomerTypes" fieldtype="many-to-one" cfc="CustomerTypes" fkcolumn="id" lazy="true" />
Then you should be able to dump an instance of a Customers object and see that it has a customerType property and hence you can write something like this:
<cfset cust = entityLoad("Customers", 1) />
<cfset type = cust.getCustomerType().getName() />
Hope that helps!

Is it possible to bind a scalar query result to a property in nhibernate?

I have an entity which has a property that does not get its value from the entity's table. But it could be calculated by a query if it is possible.
To clarify, I have an entity of an Entry and I want to check if the current user voted it or not. So for that reason I thought that I could add a property like "IsCurrentUserVoted" to the Entry entity. And if it is possible to bind its value from a query like "select count(*) from vote where userId = :currentUser".
I wonder if nhibernate supports this kind of a feature or it is the only way to join the vote and entry tables? If it does not, what would your solution be in that case.
Thanks in advance.
Yes, I think you can do that with the formula property...
<property name="IsCurrentUserVoted" formula="select count(*) from vote where userId=Id" />
I'm not positive on the syntax, you might need to use a scalar function to wrap the query. Also, it might be best to run that query only when needed, unless you really need that value every time you get an Entry.

NHibernate, Pivot-table and/or GetNextID()

Can I use pivot tables with NHibernate?
Can I write
ID id = new Transaction().GetNextID("[TableName]", "[IDColumnName]")-like codes to increment IDs with NHibernate?
Auto Increment Ids
There are auto-increment id generators available for all databases supported by NHibernate. See generator in the reference documentation.
To use an auto-increment Id property, map it as follows and leave the Id value as the default (0) when creating new entities.
<id name="Id" >
<generator class="native"/>
</id>
You can even roll your own generator by implementing NHibernate.Id.IIdentifierGenerator. However, see this question for a discussion on why you should use the database native auto-increment mechanism.
Pivot Tables
NHibernate has no specific support for Pivot tables. That said, you can map the results of any SQL query to objects using a result transformer. The target object does not need to be mapped, and the columns must match the properties.
Named SQL queries can be defined in your mapping files. You can also use native SQL queries directly. Both types of query support named and positional parameters.
var list = session.GetNamedQuery("query-name")
.SetResultTransformer( Transformers.AliasToBean<Foo>())
.SetString("name", "foo")
.List<Foo>();
var list = session.CreateSQLQuery("SELECT * FROM table WHERE A=? and B=?")
.SetString(0, "foo")
.SetInt(1, 42)
.SetResultTransformer( Transformers.AliasToBean<Foo>())
.List<Foo>();
If you defined a view for a pivot query, you could define a readonly mapped class for it with mutable="false".
Result Transformers
A number of result transformers are available, including:
Transformers.ToList Returns a list of values for each row.
Transformers.AliasToEntityMap Creates a map from column names to values for each row.
Transformers.AliasToBean() Creates an object of the nominated type per row and sets its properties with the value from the column of the same name.

Coldfusion ORM EntityLoad

When I call EntityLoad does it load ALL the data in the table or just sets up a reference or something, because say if i do this:
<cfset test = EntityLoad("Table") />
and then do:
ArrayLen(test)
I get the number of rows in the database. If its doing that then it should be loading all the data, and that is really inefficent, say I have a table with 20x10^5 rows or something, it'll be horrible to load all data just to get the length of the table.
Without seeing your code, I don't know exactly what you're talking about, but here's the run-down:
EntityLoad w/ 1 argument (the Entity name) is going to load all the Entities with that name from the DB. It does however take some additional (optional) arguments to adjust it's behavior. Instead of describing all of them here I'll point you to the beta documentation and a site giving some further exmaples:
http://help.adobe.com/en_US/ColdFusion/9.0/Developing/WS02BE9BC5-8206-434b-8486-95CD09CDB985.html#WSf01dbd23413dda0e54af3c7912012f78097-7ff8
http://www.coldfusion-ria.com/Blog/index.cfm/2009/7/22/ColdFusion-9s-ORM-Entity-Load-Functions
To do what you are trying to do efficiently without incurring a select * on your table, use HQL.
<cfset hqlQuery = "select count(*) From person" />
<cfset results = ORMExecuteQuery(hqlQuery)[1] />