Firstly, I'm sorry for asking this question AGAIN when there are several resources that (theoretically)
explain it. I've listed the references I used at the bottom of this question, hopefully they will
help someone else if nothing else.
I am trying to execute a simple stored proc on an oracle 11 database. My intent is to create
a List{T} object from data returned via a SYS_REFCURSOR.
I get this error message as soon as I try to to create an nhibernate session object:
{"Errors in named queries: {GET_COLLATERAL}"}
Here is my mapping. The namespace, schema, assembly, query name are spelled correctly.
The file is named GetCollateral.hbm.xml and is marked as an embedded resource.
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Poolman" namespace="Poolman.Entities" schema="poolman_own">
<sql-query name="GET_COLLATERAL" callable="true">
<return class="Poolman.Entities.IDNamePair">
<return-property name="ID" column="sort_order"></return-property>
<return-property name="Name" column="collateral"></return-property>
</return>
{GET_COLLATERAL(?)}
</sql-query>
</hibernate-mapping>
After quite a bit of troubleshooting I managed to get a session object to be created
by removeing the return element from the mapping as shown below. Apparently there is something wrong with it
but I don't know what.
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Poolman" namespace="Poolman.Entities" schema="poolman_own">
<sql-query name="GET_COLLATERAL" callable="true">
{GET_COLLATERAL(?)}
</sql-query>
</hibernate-mapping>
I don't expect to get a result set back with no return mapping, but using the mapping above allows nhibernate to create its session obect and to try to execute the query. However, nHibernate cannot get it's parameters right. I get this error message:
{"Expected positional parameter count: 1, actual parameters: [] [{GET_COLLATERAL(?)}]"}
I've tried:
CALL GET_COLLATERAL()
BEGIN GET_COLLATERAL(); END;
The above wrapped in CDATA
Here is my stored proc:
create or replace
PROCEDURE GET_COLLATERAL(p_cursor OUT SYS_REFCURSOR)
IS
BEGIN
OPEN p_cursor for
SELECT collateral, sort_order
FROM
(
-- Long query omitted. The query executes when pasted into a command window.
) ORDER BY sort_order ;
END;
Here is my entity class. This class does not map to any one table but I tried to create a mapping for it anyhow.
namespace Poolman.Entities
{
public class IDNamePair
{
public virtual int ID { get; set; }
public virtual string Name { get; set; }
}
}
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping assembly="Poolman"
namespace="Poolman.Entities"
schema="poolman_own"
xmlns="urn:nhibernate-mapping-2.2">
<class name="IDNamePair" table="x">
<id></id>
<property name="ID" column="sort_order"/>
<property name="Name" column="collateral"/>
</class>
</hibernate-mapping>
Here is the code I'm using to call the query:
public List<Entities.IDNamePair> GetCollateral()
{
IQuery query = (IQuery)Session.GetNamedQuery("GET_COLLATERAL");
List<Entities.IDNamePair> list = new List<Entities.IDNamePair>();
System.Collections.IList result = query.List();
list = result.OfType<Entities.IDNamePair>().ToList();
return list;
}
I really appreciate any help with this. I'm stuck.
Here are links to other resources I've found, none can help me however:
Sorry stackoverflow only allows me to post two links:
Oracle stored procedures, SYS_REFCURSOR and NHibernate
http://www.techonthenet.com/oracle/questions/cursor1.php
Maybe two considerations could be done for this problem:
First, the Stored Procedure which has no input parameters, does not require to be called with parameters, so as this questions comment says calling sp with out ref cursor call the SP in this way { call GET_COLLATERAL }
Second, to use a "not mapped" class as result set, you should instruct nhibernate about that "not mapped" class, so try to add <import class="FullClassName" rename="ClassNameMayBeRenamed"/> at top of the mapping file
So this could be a mapping for this SP:
....
<import class="Poolman.Entities.IDNamePair" />
....
<sql-query name="GET_COLLATERAL" callable="true">
<return class="Poolman.Entities.IDNamePair">
<return-property name="ID" column="sort_order"></return-property>
<return-property name="Name" column="collateral"></return-property>
</return>
{ call GET_COLLATERAL}
</sql-query>
The calling code could be a simple code to call a SP through NHibernate..
I hope this could be your solution. Please let me know if I'm wrong
Regards
Related
This method gets called.
public IList<MyStuff> GetMyStuff(Int64 MyStuffId)
{
ICriteria criteria = NHibernateSessionManager.Instance.GetSession().CreateCriteria(typeof(MyStuff));
criteria.Add(Expression.Eq("x", MyStuff));
return criteria.List<MyStuff>();
}
But if I profile SQL Server, I can see that NHibernate doesn't try to access the server.
No errors are thrown. It is just the criteria.List() simply returns 0 rows.
MyStuff is a class
public class MyStuff {
public virtual int Id { get; set; }
public virtual int x { get; set; }
... more attributes ....
public override int GetHashCode() {
return (GetType().FullName + "|" + Id.ToString()).GetHashCode();
}
}
And MyStuff is a HBM mapping:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" auto-import="false" assembly="MyStuff" namespace="My.Stuff" default-lazy="false">
<class name ="MyStuff" table="dbo.viewMyStuff" dynamic-update="false" lazy="false">
<cache usage="read-only"/>
<id name="Id" column="Id" type="int">
<generator class="native" />
</id>
<property name="x" />
.... other properties
</class>
</hibernate-mapping>
The following works just:
select * from viewMyStuff
NHibernate does just fine with other classes/views in the same project.
In fact if I intentionally typo the "table" in the HBM file to "XviewXMyStuffX" NHibernate doesn't have any problem with the typo. Why is NHibernate simply ignoring the expected attempt to access my database view?
I turns out that the view treats the attribute "x" as a string. But in nHibernate I define it as a Int64. These type differences much be causing the criteria to fail. But without any reported error?
Double check that your query really tries to use the exact class you intenden, and that the mapping also applies to exactly the same class. Beware of classes with same name in different namespace or assembly, for instance. One cause of this type of issue is if you attempt to query for a class that is in fact not mapped in NHibernate - then NHibernate will return en empty result, and not an error.
Oh, and have you tried without the cache-element to rule that out?
Consider this class that represents a node in a hierarchical structure:
public class Node
{
public Node()
{
Children = new List<Node>();
}
public virtual int Id { get; set; }
public virtual IList<Node> Children { get; set; }
public virtual Node Parent { get; set; }
public virtual int Position
{
get { return Parent == null ? -1 : Parent.Children.IndexOf(this); }
set { }
}
}
The mapping looks like this (as NHibernate does not support lists in bidirectional associations, I use a bag here and have the children determine their position automatically):
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" namespace="AmbiguousColumn" assembly="AmbiguousColumn" xmlns="urn:nhibernate-mapping-2.2">
<class name="Node">
<id name="Id" type="Int32">
<generator class="identity" />
</id>
<bag name="Children" inverse="true" cascade="all" order-by="Position">
<key column="Parent" />
<one-to-many class="Node" />
</bag>
<many-to-one name="Parent" />
<property name="Position" />
</class>
</hibernate-mapping>
To get all nodes with their children loaded I'd use a query like this:
var nodes = session.QueryOver<Node>()
.Fetch(x => x.Children).Eager
.List();
However, executing this results in an exception:
NHibernate.Exceptions.GenericADOException: could not execute query
[...(sql)...] ---> System.Data.SqlClient.SqlException: Ambiguous column name 'Position'.
The SQL:
SELECT
this_.Id as Id0_1_,
this_.Parent as Parent0_1_,
this_.Position as Position0_1_,
children2_.Parent as Parent3_,
children2_.Id as Id3_,
children2_.Id as Id0_0_,
children2_.Parent as Parent0_0_,
children2_.Position as Position0_0_
FROM
Node this_
left outer join
Node children2_
on this_.Id=children2_.Parent
ORDER BY
Position
I understand why this happens: NH joins the same table twice, but uses the order clause without qualifying the column name.
The question is: how can I make this scenario work? Resorting to instead of is probably difficult as I'd like to have a bidirectional relation.
There are a couple of similar question on SO, but nowhere did I find an actual solution.
Update: the error is database/driver specific. Using the Sql Server CE (e.g. SqlServerCeDriver and MsSqlCe40Dialect) I get the proper query. Using Sql Server (e.g. Sql2008ClientDriver and MsSql2012Dialect) produces the unqualified queries.
According to my own tests, this behavior still exists in the master branch on github.
A gist with a test case: https://gist.github.com/anonymous/5377535
I think I found the cause of the problem and viable workarounds:
The cause of the issue is the fact that the column is called "Position", which is a reserved word in ODBC according to http://msdn.microsoft.com/en-us/library/ms189822.aspx
This combined with the fact that the default value for NH's hbm2ddl.keywords property is set to "keywords" somehow caused NH not to qualify the order-by clause, probably because it though "Position" was a keyword, not a column.
http://nhforge.org/blogs/nhibernate/archive/2009/06/24/auto-quote-table-column-names.aspx
Ways to fix it:
1) Use a different name for the property - one that isn't a keyword. In this case, PositionInParent would have worked without any issues.
2) Quote the order by clause properly using back-ticks.
<bag name="Children" inverse="true" cascade="all" order-by="`Position`">
Or whatever it takes in your mapping API of choice, e.g. in mapping by code:
cls.Bag(x => x.Children,
map =>
{
map.Inverse(true);
map.Cascade(Cascade.All);
map.Key(key => key.Column("Parent"));
map.OrderBy("`Position`"); // note that you must not use a lambda expression in this case
},
map => map.OneToMany());
3) Disable keyword auto import, ie. set hbm2ddl.keywords to none (neither keywords nor auto-quote will work):
<property name="hbm2ddl.keywords">none</property>
Or programmatically:
config.DataBaseIntegration(db => db.KeywordsAutoImport = Hbm2DDLKeyWords.None);
You can still auto-quote reserved words by calling SchemaMetadataUpdater.QuoteTableAndColumns just before building the session factory.
SchemaMetadataUpdater.QuoteTableAndColumns(config);
I'll stick with approach 3 for now as it is the most painless so far.
I'm having a hard time trying to get my stored procedure works with NHibernate. The data returned from the SP does not correspond to any database table.
This is my mapping file:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DomainModel" namespace="DomainModel.Entities">
<sql-query name="DoSomething">
<return class="SomeClass">
<return-property name="ID" column="ID"/>
</return>
exec [dbo].[sp_doSomething]
</sql-query>
</hibernate-mapping>
Here is my domain class:
namespace DomainModel.Entities
{
public class SomeClass
{
public SomeClass()
{
}
public virtual Guid ID
{
get;
set;
}
}
}
When I run the code, it fails with
Exception Details: NHibernate.HibernateException: Errors in named queries: {DoSomething}
at line 80
Line 78: config.Configure(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "NHibernate.config"));
Line 79:
Line 80: g_sessionFactory = config.BuildSessionFactory();
When I debug into NHibernate code, it seems that SomeClass is not added to the persister dictionary because there isn't a class mapping (only sql-query) defined in hbm.xml. And later on in CheckNamedQueries function, it is not able to find the persistor for SomeClass.
I've checked all the obvious things (e.g. make hbm as an embedded resource) and my code isn't too much different from other samples I found on the web, but somehow I just can't get it working. Any idea how I can resolve this issue?
Well, where is your class mapping for SomeClass?
You still need to map it. Read http://nhibernate.info/doc/nh/en/index.html#querysql-load.
Look at using a class mapping with a subselect block. I found this in the Java documentation but maybe it will work for .Net too.
http://docs.jboss.org/hibernate/core/3.3/reference/en/html/mapping.html (scroll down to section 5.1.3)
I have the following named SQL query defined:
<sql-query name="ItemSearch">
<return class="ItemSearchResult">
<return-property name="Item" column="ItemId" />
<return-property name="Distance" column="Distance" />
</return>
SELECT
Items.*,
dbo.DistanceBetween(Latitude, Longitude, :lat, :long) AS Distance
FROM Items
WHERE Contains(Name, :keywords)
ORDER BY Distance ASC
</sql-query>
Whenever I try to run my application, I get the generic error "Errors in named queries: {ItemSearch}". Is there something obviously wrong here?
The ItemSearchResult class is a very simple wrapper class that looks like this:
public class ItemSearchResult
{
public Item Item {get; set;}
public double Distance {get; set;}
}
Do you have a correct .hbm.xml for ItemSearchResult? If you use ItemSearchResult in your query, then you need to have a .hbm.xml for it. Just like entities.
Here is a sample from my code:
The only few things that are different between the NHibernate version and my Hibernate is the auto-import and I would assume the package.
<hibernate-mapping auto-import="true" package="PackageName">
<class name="Name of class to maptop">
<composite-id>
<key-property name="<name of parameter>" type="TYPE"/>
</composite-id>
<property name="COLUMNNAME" type="TYPE"/>
</class>
<sql-query name="queryName">
<return alias="dr" class="Name of class to map to"/>
select columnName as {dr.nameofColumn},
from table
</sql-query>
</hibernate-mapping>
I think the problem that exists in your code is that you did not specifically map out the columns and how they map to your class.
Note: If there are any fields that do not correspond to the NHibernate XML format let me know through the comments. I don't have access to my NHibernate mapping files right now.
I have a basic Customer/Order/OrderItem/Product object graph. Customer has Many Orders, Order has Many Order Items, Product has many Order Items. These are successfully mapped using FNH.
I've hit a snag with configuring a stored procedure & fluent-nhibernate. There is not a native way to map stored procedures in fluent-hibernate FNH (version 1.0 RTM). There was a solution here about adding parts to class mappings but the AddPart call has been taken out of the release of FNH.
The stored procedure is simple:
CREATE PROCEDURE [dbo].[OrderCountByCustomer]
AS
BEGIN
SET NOCOUNT ON;
SELECT
c.name as [Customer.Name],
CAST(count(o.id) as NVARCHAR) as [Customer.OrderCount]
FROM customer c
LEFT OUTER JOIN [order] o
ON o.customer_id = c.id
GROUP BY c.name
END
There's a CustomerOrderSummary.hbm.xml in
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NVAble.Orders.Core" namespace="NVAble.Orders.Core">
<sql-query name="OrderSummary">
<return class="CustomerOrderSummary">
<return-property column="Customer.Name" name="CustomerName" />
<return-property column="Customer.OrderCount" name="OrderCount" />
</return>
EXEC [OrderCountByCustomer]
</sql-query>
</hibernate-mapping>
Here is the CustomerOrderSummary class def:
namespace NVAble.Orders.Core
{
public class CustomerOrderSummary
{
virtual public string CustomerName { get; set; }
virtual public string OrderCount { get; set; }
public override string ToString()
{
return string.Format("{0} {1}", CustomerName, OrderCount);
}
}
}
However, when try to start a NH session i get error in named query OrderSummary with no other details.
I'm probably missing something really simple that maps the CustomerOrderSummary class to the procedure, I don't know. That domain object obviously doesn't map directly to a table in the data base so I'm unsure if having a normal <class /> HBM mapping would work?
Thanks in advance!
Ok, so after a bit more investigation. I needed a mapping for the Domain Class as well as a named query hbm.xml file.
In my configure class i have
config.Mappings(x =>
{
x.FluentMappings.AddFromAssemblyOf<CustomerMapping>().ExportTo(schemaPath);
x.HbmMappings.AddFromAssemblyOf<CustomerOrderSummary>();
});
Only downside is that I need to manually create the xml mapping for the stored procedure, I can't use FNH at the current time