named query in NHibernate with not equal operator - nhibernate

NHibernate throw exception with sql server not equal to operator <>.
<sql-query name="Select">
<return alias="OrderStock" class="OrderStock"/>
select * from OrderStock WHERE dh.DATE_UNLOADED <> '1753-01-01'
</sql-query>
Name cannot begin with the '>' character

We can escape symbols <> with <> like this:
<sql-query name="Select">
<return alias="OrderStock" class="OrderStock"/>
select * from OrderStock WHERE dh.DATE_UNLOADED <> '1753-01-01'
</sql-query>
Or we can use xml raw text escaping with <![CDATA[ .... ]]>:
<sql-query name="Select">
<return alias="OrderStock" class="OrderStock"/>
<![CDATA[
select * from OrderStock WHERE dh.DATE_UNLOADED <> '1753-01-01'
]]>
</sql-query>

Related

NHibernate GetNamedQuery executes stored procedure but returns empty list

I have a Stored Procedure that returns 2 records when tested in SQL Studio Management Express using this script:
declare #route_id_param as varchar(10), #start_time as datetime, #start_date as datetime, #end_date as datetime
set #start_time = GETDATE()
set #start_date = CONVERT(DATETIME,'10/26/2013',101)
set #end_date = CONVERT(DATETIME,'12/26/2020',101)
exec dbo.sp_get_deactivation_list #companyId=1, #startDate = #start_date, #endDate = #end_date;
select execution_time_in_ms = DATEDIFF(millisecond, #start_time, getdate())
GO
When I attempt to executed same stored procedure from NHibernate, using the following mapping file, I get 0 results.
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping
xmlns="urn:nhibernate-mapping-2.2"
assembly="HGT.IridiumAirtime.Service"
namespace="HGT.IridiumAirtime.Service.Model">
<sql-query name="GetDeactivationList" callable="true">
<query-param name="companyId" type="int"/>
<query-param name="startDate" type="DateTime"/>
<query-param name="endDate" type="DateTime"/>
<!--<return class="Activation">
<return-property column="MobileId" name="MobileId" />
<return-property column="RadioAddress" name="RadioAddress" />
<return-property column="DeactivationDate" name="DeactivationDate" />
</return>-->
exec [sp_get_deactivation_list] #companyId=:companyId, #startDate=:startDate, #endDate=:endDate
</sql-query>
</hibernate-mapping>
The method that executed said Stored Procedure is:
public IEnumerable<TOut> ExecuteStoredProcedure<TOut>(string procedureName, IList<SqlParameter> parameters)
{
IEnumerable<TOut> result;
using (var session = _sessionFactory.OpenSession())
{
var query = session.GetNamedQuery(procedureName);
foreach (var parameter in parameters)
{
query.SetParameter(parameter.ParameterName, parameter.Value);
}
AddStoredProcedureParameters(query, parameters);
result = query.List<TOut>();
}
return result;
}
I even created a new mapping file for this Activation type even though there is no associated table but NHibernate does not return the correct result. Un-commenting the type mapping definition within the procedure's mapping file does not change outcome. Am I missing something here?
I get no exceptions, so it looks like procedure is being executed.
UPDATE: 1 Removed call to AddStoredProcedureParameters and replaced with method body.
UPDATE 2 Adding Stored Procedure:
if OBJECT_ID ( 'dbo.[sp_get_deactivation_list]', 'P' ) is not null
drop procedure dbo.[sp_get_deactivation_list];
go
create procedure [dbo].[sp_get_deactivation_list]
#companyId int,
#startDate DateTime,
#endDate DateTime
as
begin
select
assMobileRadio.Mobile_ID as MobileId,
tblRadioinfo.Radio_Address as RadioAddress,
tblRadioinfo.Deactivation_Date as DeactivationDate
from tblRadioinfo
left join assMobileRadio
on tblRadioinfo.Radio_ID = assMobileRadio.Radio_ID
where tblRadioinfo.Radio_Type_ID in (2, 4, 7)
and tblRadioinfo.Company_ID = #companyId
and tblRadioinfo.Deactivation_Date <= #endDate
and tblRadioinfo.Deactivation_Date >= #startDate
and tblRadioinfo.Radio_Address in (select IMEI from [airtime_cdrs] where Effective_Date > #startDate and Effective_Date < #endDate)
from airtimes_cte for xml path('')),1,1,''))
ORDER BY tblRadioinfo.Deactivation_Date
end
I've just tried your mapping (the same as possible) and your 1) mapping and 2) call is OK. Except the fact that I am not sure about the call inside of the AddStoredProcedureParameters
So, this is working:
var query = session.GetNamedQuery("GetDeactivationList");
query.SetParameter("companyId", 1);
query.SetParameter("startDate", new DateTime(2013,10,26));
query.SetParameter("endDate", new DateTime(2020,12,26));
// if the <return class="Activation"> is commented ... we'll get object[]
var list = query.List<object[]>();
And this is working. Other words. Your mapping is working. The SQL call (exec sp) is fired, because if the named query won't be find, or some other issues would happen, we will get an Exception.
The only suspected right now is the black box AddStoredProcedureParameters (could not it switch start and end date?)
I would suggest, do check your Unit test in the SQL Server Profiler, you will see what is passed to DB... and you can take and reexecute.
If executing the stored procedure you have is your main purpose (not exactly using GetNamedQuery), you can use the following:
Assuming that your methods' arguments are: int? id, DateTime? fromDate, DateTime? toDate (all nullable values)
IList<TOut> result = session.CreateSQLQuery("exec [dbo].[sp_get_deactivation_list]:param1, :param2, :param3")
.SetParameter("param1", id, NHibernateUtil.Int32)
.SetParameter("param2", fromDate, NHibernateUtil.DateTime)
.SetParameter("param3", toDate, NHibernateUtil.DateTime)
.SetResultTransformer(Transformers.AliasToBean<TOut>()).List<TOut>();
return result;
And in your mapping file for class TOut, just have the related properties as normal.
If Activation is non-managed entity with nhibernate you can use this:
var query = session.GetNamedQuery(procedureName);
AddStoredProcedureParameters(query, parameters);
result = query.SetResultTransformer(Transformers.AliasToBean(typeof(TOut)))
.List<TOut>();
If does try to add alias:
<return alias="activation" class="Activation">
...
</return>
For example:
<sql-query name="mySqlQuery">
<return alias="person" class="eg.Person">
<return-property name="Name" column="myName"/>
<return-property name="Age" column="myAge"/>
<return-property name="Sex" column="mySex"/>
</return>
SELECT person.NAME AS myName,
person.AGE AS myAge,
person.SEX AS mySex,
FROM PERSON person WHERE person.NAME LIKE :name
</sql-query>
Reference here are some more ways to solve your issue.
Just you need how to say to nhibernate how return the data, "this" column "resolve this" property.
Sorry for my poor english.

nibernate call mysql stored procedure

I have a stored procedure with two parameters that returns an int.
my mapping.hbm.xml
<sql-query name="CreateAttribute">
<query-param name="idAttrType" type="int"/>
<query-param name="idSystemUser" type="int"/>
call CreateAttribute :idAttrType,:idSystemUser
</sql-query>
C# usage
var t = session.GetNamedQuery("CreateAttribute");
t.SetInt32("idAttrType", 12);
t.SetInt32("idSystemUser",int.Parse(id));
var result = t.List();
Then i receive the folowing:
[SQL: call CreateAttribute ?p0,?p1] ---> MySql.Data.MySqlClient.MySqlException: You have an error in your SQL syntax;
Any suggestions?
Should it not be:-
<sql-query name="CreateAttribute">
<query-param name="idAttrType" type="int"/>
<query-param name="idSystemUser" type="int"/>
call CreateAttribute(:idAttrType,:idSystemUser);
</sql-query>
Notice the braces around the params.
edit run this against your MySql database, does it run?
call CreateAttribute(0,0);
Modified the call as following
SELECT call CreateAttribute(0,0); -- it worked
new mapping now is
<sql-query name="CreateAttribute">
<query-param name="idAttrType" type="int"/>
<query-param name="idSystemUser" type="int"/>
select CreateAttribute(:idAttrType,:idSystemUser);
</sql-query>

Using a named query in a hbm with import class

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'/>

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.

where clause as a parameter

IN converting over a legacy application we need to convert named query to nhibernate. The problem is that the where clause is being set.
here is the mapping
<resultset name="PersonSet">
<return alias="person" class="Person">
<return-property column="id" name="Id" />
<return-property column="ssn" name="Ssn" />
<return-property column="last_name" name="LastName" />
<return-property column="first_name" name="FirstName"/>
<return-property column="middle_name" name="MiddleName" />
</return>
</returnset>
<sql-query name="PersonQuery" resultset-ref="PersonSet" read-only="true" >
<![CDATA[
SELECT
person.ID as {person.Id},
person.SSN as {person.Ssn},
person.LAST_NAME as {person.LastName},
person.MIDDLE_NAME as {person.MiddleName},
person.FIRST_NAME as {person.FirstName},
FROM PERSONS as person
where :value
]]>
</sql-query>
and the c# code:
String query = "person.LAST_NAME = 'Johnson'";
HibernateTemplate.FindByNamedQueryAndNamedParam("PersonQuery", "value", query);
The error:
where ?]; ErrorCode []; An expression of non-boolean type specified in a context where a condition is expected, near '#p0'.
This doesn't work because you try to replace :value with "person.LAST_NAME = 'Johnson'" wanting that the query becomes
SELECT person.ID, person.SSN, person.LAST_NAME, person.MIDDLE_NAME, person.FIRST_NAME
FROM PERSONS as person
where person.LAST_NAME = 'Johnson'
This won't work. You can only replace the 'Johnson' part dynamically not the whole condition. Thus what really gets generated is
SELECT person.ID, person.SSN, person.LAST_NAME, person.MIDDLE_NAME, person.FIRST_NAME
FROM PERSONS as person
where 'person.LAST_NAME = \'Johnson\''
Which obviously isn't a valid condition for the WHERE-part as there is only a literal but no column and operator to compare the field to.
If you only have to match against person.LAST_NAME rewrite the xml-sql-query to
<sql-query name="PersonQuery" resultset-ref="PersonSet" read-only="true" >
<![CDATA[
SELECT
...
FROM PERSONS as person
where person.LAST_NAME = :value
]]>
</sql-query>
And in the C# code set
String query = "Johnson";
If you need to dynamically filter by different columns or even multiple columns at a time use filters. e.g. like this (i made a few assumptions on you hibernate-mapping file)
<hibernate-mapping>
...
<class name="Person">
<id name="id" type="int">
<generator class="increment"/>
</id>
...
<filter name="ssnFilter" condition="ssn = :ssnValue"/>
<filter name="lastNameFilter" condition="lastName = :lastNameValue"/>
<filter name="firstNameFilter" condition="firstName = :firstNameValue"/>
<filter name="middleNameFilter" condition="middleName = :middleNameValue"/>
</class>
...
<sql-query name="PersonQuery" resultset-ref="PersonSet" read-only="true" >
...
FROM PERSONS as person
]]>
</sql-query>
<!-- note the missing WHERE clause in the PersonQuery -->
...
<filter-def name="ssnFilter">
<filter-param name="ssnValue" type="int"/>
</filter-def>
<filter-def name="lastNameFilter">
<filter-param name="lastNameValue" type="string"/>
</filter-def>
<filter-def name="middleNameFilter">
<filter-param name="midlleNameValue" type="string"/>
</filter-def>
<filter-def name="firstNameFilter">
<filter-param name="firstNameValue" type="string"/>
</filter-def>
</hibernate-mapping>
Now in your code you should be able to do
String lastName = "Johnson";
String firstName = "Joe";
//give me all persons first
HibernateTemplate.FindByNamedQuery("PersonQuery");
//just give me persons WHERE FIRST_NAME = "Joe" AND LAST_NAME = "Johnson"
Filter filter = HibernateTemplate.enableFilter("firstNameFilter");
filter.setParameter("firstNameValue", firstName);
filter = HibernateTemplate.enableFilter("lastNameFilter");
filter.setParameter("lastNameValue", lastName);
HibernateTemplate.FindByNamedQuery("PersonQuery");
//oh wait. Now I just want all Johnsons
HibernateTemplate.disableFilter("firstNameFilter");
HibernateTemplate.FindByNamedQuery("PersonQuery");
//now again give me all persons
HibernateTemplate.disableFilter("lastNameFilter");
HibernateTemplate.FindByNamedQuery("PersonQuery");
If you need still more dynamic queries (e.g. even changing the operator (=, !=, like, >, <, ...) or you have to combine restrictions logically (where lastname = "foo" OR firstname" = "foobar") then it's definitly time to look into the
Hibernate Criteria API
I'm unfamiliar with this HibernateTemplate syntax, but it looks like you're querying the original fieldname in SQL rather than the alias. Try this:
String query = "person.LastName = 'Johnson'";
or, maybe:
String query = "[person.LastName] = 'Johnson'";
or, possibly:
String query = "{person.LastName} = 'Johnson'";
Depends what sort of pre-processing is going on before the final SQL query is sent to the server.
That's because :value is a bind variable in the query; you cannot simply replace it with a string that contains an arbitrary string (that would become part of the query), only with an actual value. In your case, the value is "person.LAST_NAME = 'Johnson'", which is actually a string, not a boolean value. Boolean values would be true or false, both of which are rather useless for what you try to archive.
Bind variables more-or-less replace literals, not complex expressions.