Using a named query in a hbm with import class - nhibernate

In my MSSQL server I have a SQL view called AllFavourite. In order to load the data into my DTO class I have the following in my hbm.xml file...
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="Domain.Model.Entities" assembly="Domain.Model">
<import class="AllFavourite"/>
</hibernate-mapping>
In my code I have the following.
public IList<AllFavourite> GetFavourites(int userId)
{
var query = Session
.CreateSQLQuery("SELECT * FROM AllFavourite where UserId=:UserId")
.SetInt32("UserId", userId)
.SetResultTransformer(new AliasToBeanResultTransformer(typeof(AllFavourite)));
return query.List<AllFavourite>();
}
This works great and produces the results that I am after, however I would like to move the SQL from code into a named query into the hbm.xml file. So my hbm.xml file now looks like this
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="Domain.Model.Entities" assembly="Domain.Model">
<import class="AllFavourite"/>
<query name="GetAllFavouriteByUserId">
<![CDATA[
SELECT * FROM AllFavourite WHERE UserId=:UserId
]]>
</query>
</hibernate-mapping>
and my code now looks like this
public IList<AllFavourite> GetFavourites(int userId)
{
var query = Session
.GetNamedQuery("GetAllFavouriteByUserId")
.SetInt32("UserId", userId)
.SetResultTransformer(new AliasToBeanResultTransformer(typeof(AllFavourite)));
return query.List<AllFavourite>();
}
However when I run this I get an error:-
Parameter UserId does not exist as a
named parameter in [SELECT * FROM
AllFavourite WHERE UserId=:UserId]
So my question is it possible to use a named query in this manner?

The query tag expects a HQL query:
<query name="GetAllFavouriteByUserId">
<![CDATA[
from AllFavourite where UserId = :UserId
]]>
</query>
If you want to write a native sql query you should use the sql-query tag:
<sql-query name="GetAllFavouriteByUserId">
<return alias="foo" class="Foo"/>
<![CDATA[
SELECT {foo.ID} as {foo.ID},
{foo}.NAME AS {foo.Name}
FROM sometable
WHERE {foo}.ID = :UserId
]]>
</sql-query>

Don't you need this?
<query-param name='UserId' type='Integer'/>

Related

How to map an NHibernate entity to a query

I've seen a number of examples of this, and as far as I can tell my HBM file follows the same pattern, but it's not working. First, the file:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping default-cascade="save-update" xmlns="urn:nhibernate-mapping-2.2">
<class name="ThinAir" mutable="false" lazy="true" >
<id name="JobId">
<generator class="native" />
</id>
<property name="UserLogin"/>
<property name="UserEmail"/>
<property name="DateProcessed"/>
<loader query-ref="myquery"/>
</class>
<sql-query name="myquery">
<return class="ThinAir">
<return-property name="JobID" column="JobId"/>
<return-property name="userLogin" column="UserLogin"/>
<return-property name="DateProcessed" column="DateProcessed"/>
<return-property name="userEmail" column="UserEmail"/>
</return>
<![CDATA[
SELECT DISTINCT JobID,
userLogin,
DateProcessed,
useremail
FROM dbo.someothertable
]]>
</sql-query>
</hibernate-mapping>
"myquery" in-and-of-itself, works. That is to say, if I call
var x = session.GetNamedQuery("myquery").List();
I get a correct List of ThinAir objects.
But, when I try to get a list of ThinAirs like this:
var submissions = session.CreateCriteria<ThinAir>().List<ThinAir>();
I get
Test method testThinAir threw exception:
NHibernate.Exceptions.GenericADOException: could not execute query
[ SELECT this_.JobId as JobId16_0_, this_.UserLogin as UserLogin16_0_, this_.UserEmail as UserEmail16_0_, this_.DateProcessed as DateProc4_16_0_ FROM ThinAir this_ ]
My interpretation of this phenomenon is that NH is ignoring my <loader> tag and so trying to load the data from the underlying table, which by default it assumes to be named ThinAir because that's the name of the entity class, only there isn't any ThinAir table, hence the error message.
Is that interpretation correct? And in any case, what am I doing wrong and how can I do it right?
Thanks in advance.
Michael
One way how to achieve this, would be to move the mapping from a query into the subselect:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping default-cascade="save-update" xmlns="urn:nhibernate-mapping-2.2">
<class name="ThinAir" mutable="false" lazy="true" >
<subselect>
<![CDATA[
SELECT DISTINCT JobID,
userLogin,
DateProcessed,
useremail
FROM dbo.someothertable
]]>
</subselect>
... // rest of the mapping

nHibernate named query, result transformation and column name with whitespace

I've got a named nHibernate query which returns custom data. So I decided to make a bean class to encapsulate data. Here is some code:
public IList<Report> GetReport(int reportId)
{
return Session.GetNamedQuery("GetReport")
.SetParameter("Id", reportId)
.List<Report>();
}
public class Report
{
public virtual string Id { get; set; }
...
public virtual string CustomColumn { get; set; }
}
and mapping:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="MyAssembly"
namespace="MyAssembly.Model">
<class name="Report" table="Report">
<id name="Id" column="Id">
<generator class="assigned"/>
</id>
...
<property column="`Custom column`" name="CustomColumn" />
</class>
<sql-query name="GetReport">
<return class="Report"/>
<query-param name="Id" type="int" />
exec GetReport :Id
</sql-query>
</hibernate-mapping>
But when I invoke this method I got an exception:
NHibernate.Exceptions.GenericADOException : could not execute query
----> System.IndexOutOfRangeException : [Custom column]
Help, anyone?
Ok, I found the problem myself. I thought, that I need to escape column names which contains spaces with ` as I do for table names. But apparently I shouldn't.
This syntax is fine.
<property column="Custom column" name="CustomColumn" />

nHibernate and concurrency check

I want to achieve concurrency check using nHibernate 3 using UnitOfWork pattern.
To be more precise:
open new session session,
load entity in a session,
close session,
give user some time to edit data in loaded entity,
open new session,
update data
close session.
I'm using timestap to version entity.
Here is my mapping file
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="...."
namespace="...."
default-lazy="false">
<class name="Employee"
optimistic-lock="version"
dynamic-update="true">
<id name="Id">
<generator class="native" />
</id>
<version column="LastEditDate" generated="always" type="timestamp" />
<property name="Name" not-null="1" length="255" />
<property name="LastEditUser" not-null="1" length="255"/>
</class>
</hibernate-mapping>
I have no idea how to update entity in session context
var entity = <updated by user>
using (var session = GetNewSession())
{
//todo: load current version value / attach entity to context
session.SaveOrUpdate(entity);
//if concurency check fails, StaleObjectException (or similar) is expected to be thrown
}
In SQL it should work like this
UPDATE ENTITY SET LastEditDate = #P1, ... WHERE ID = #P2 AND LastEditDate = #P3
where:
#P1 - new LastEditDate
#P2 - entity ID
#P3 - previous LastEditDate
If ROWSMODIFIED = 1 then update was successfull, else if = 0 then ConcurrencyException
Using Linq2Sql it was very simple: create versioning column, attach entity to new session context and try to update.
How can I do it in nHiberate? Is it supported?
session.Update(entity)
should be enough.
I think you should use session.Lock(entity,LockMode.Update).

using a View in a Named Query

I have a named Query that uses a view. I am unable to create a class mapping because this view does not have a Id column. I created a named query and set it to return it as a class, defining all the return values. However, I still receive a KeyNotFound Exception. If I set all of the columns to It returns the List. How can you tell NHibernate to map this to a class.
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<sql-query name="GetAvailablePermissions" read-only="true">
<return alias="perm" class="Domain.AcsAvailablePermission, Domain">
<return-property name="Id" column="PermissionId"/>
<return-property name="Code" column="Code"/>
<return-property name="Category" column="Category"/>
<return-property name="Description" column="Description"/>
</return>
<![CDATA[
SELECT
[PermissionId]
, [Code]
, [Category]
, [Description]
FROM [dbo].[vw_Permission]
WHERE
[SiteId] = :SiteId
]]>
</sql-query>
</hibernate-mapping>
I did not find a NHibernate way so I changed everything to return-scalar. Then took the IList and converted it to a IList using LINQ.

Named queries with nHibernate

I am having a great deal of trouble getting named queries to work with nHibernate. My latest problem is getting the error message "could not execute query" with no additional information. Are there any complete examples I can download from somewhere because all the tutorials and documentation examples provide code snippits but only tell half the story about getting it to work.
Here is the code that is giving me problems.
Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Model.Entities
{
public class TableInfo
{
public string TABLENAME { get; set; }
public string COLUMNNAME { get; set; }
#region Overrides
public override int GetHashCode()
{
int result = TABLENAME.GetHashCode();
result += COLUMNNAME.GetHashCode();
return result;
}
public override bool Equals(object obj)
{
if (obj == null) return false;
TableInfo dict = (TableInfo)obj;
return
dict.TABLENAME.IsEqual(this.TABLENAME) &&
dict.COLUMNNAME.IsEqual(this.COLUMNNAME);
}
#endregion
}
}
Mapping File
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Model.Entities" assembly="Model" default-lazy="false">
<class name="Model.Entities.TableInfo, Model" table="UIM_TableColumnInfo">
<composite-id>
<key-property name="TABLENAME" column="TABLENAME" type="string"></key-property>
<key-property name="COLUMNNAME" column="COLUMNNAME" type="string"></key-property>
</composite-id>
</class>
<sql-query name="GetTableInfo">
<return alias="tableInfo" class="Model.Entities.TableInfo, Model">
<return-property name="TABLENAME" column="TABLENAME"/>
<return-property name="COLUMNNAME" column="COLUMNNAME"/>
</return>
<![CDATA[
select
info.tci_table_name TABLENAME
, info.tci_column_name COLUMNNAME
from ALL_TAB_COLS c
,( select 'DATE' TYPE_NAME, 'D' data_type_ind from dual
union select 'NUMBER','N' from dual
union select 'VARCHAR2','S' from dual
) ct
, UIM_TableColumnInfo info
where c.DATA_TYPE = ct.TYPE_NAME (+)
and c.column_id is not null
and UPPER(c.TABLE_NAME) = :TableName
and UPPER(c.COLUMN_NAME) = UPPER(info.tci_column_name (+))
order by c.column_id
]]>
</sql-query>
</hibernate-mapping>
Calling Code
public List<TableInfo> GetTableInfo(string tableName)
{
return m_TableInfoRepository
.NamedQuery("GetTableInfo")
.SetString("TableName", tableName)
.List<TableInfo>() as List<TableInfo>;
}
I assume that you have tested before the SQL in your client database, so I think that maybe we should see what is happening inside, so I can recommend you this links;
Named Query Error
Using NHibernate and Log4Net in ASP.NET 2.0 applications
How do I view the SQL that is generated by nHibernate?
Hope it helps.
The inner exception should provide the actual sql that was generated and tried to run. Paste this into a database query and run it directly in the database. This will help guide you. It will be a lot easier once you know why the SQL could not be executed
Maybe I'm wrong but it seems that could be a conflict between the table "TABLENAME" and the parameter ":TableName", what happens if you try to use another parameter name?