"Ambiguous column name" exception when using order-by in collection mapping - nhibernate

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.

Related

NHibernate not calling SQL Server

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?

NHIbernate lazy load a parent object

I have two objects, Case and Note. A Case can have gobs of Notes, like, in the thousands. We are trying to load them asynchronously, in batches, and stream them to the UI so there is no delay waiting for them all to load.
The class/mappings are
public class Case
{
public virtual IList<Note> Notes { get; protected set; }
}
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="SCMS.TAMS.BusinessEntities" namespace="SCMS.TAMS.BusinessEntities">
<class name="Case" table="Cases">
<bag name="Notes" inverse="true" cascade="all" lazy="true">
<key column="CaseID" />
<one-to-many class="Note" />
</bag>
</class>
</hibernate-mapping>
public class Note
{
public virtual Case Case {get; set;}
public virtual long CaseId {get; set;}
}
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="SCMS.TAMS.BusinessEntities" namespace="SCMS.TAMS.BusinessEntities" default-lazy="true">
<class name="Note" table="CaseNotes">
<many-to-one name="Case" column="CaseID"/>
<property name="CaseId" column="CaseID" />
</class>
</hibernate-mapping>
Now, when I call
NHibernateSession.Query<Note>().Where(n => n.CaseId == 123).Skip(0).Take(10).ToList();
to load the first 10 Notes for Case 123, the thing loads the Case object, which takes about 30 seconds because there's lots of other things on it, and other logic when it gets loaded, etc., none of which I need/want at this time. All I want/need are the 10 Notes.
I've tried all sorts of variations on this mapping and none of them have worked. What am I doing wrong here?
How are you using this query? is it some thing for the UI? liking showing in a grid or something? or are you performing business logic in a component?
Either way you want to project into another object. Your query right now returns a list of notes which is then going to load that parent object per the mappings.
So if you are using this query to send the information to the UI of an asp.net mvc application, project directly into your view model
NHibernateSession.Query<Note>().Where(n => n.CaseId == 123).Select(n => new SomeViewModel { Prop1 = n.Prop1, Prop2 = n.Prop2 ...}).Skip(0).Take(10).ToList();
or create an anonymous object
NHibernateSession.Query<Note>().Where(n => n.CaseId == 123).Select n => new { n.Prop1, n.Prop2, ...}).Skip(0).Take(10).ToList();
This will keep the parent object from loading. It also has the added benefit that you are only querying the information you need because the query be limited to the data you are projecting.
Important to know is that if all above is true...
this is the real mapping (which is not it is just an obvious extract)
<class name="Note" table="CaseNotes">
<many-to-one name="Case" column="CaseID"/>
...
this is the class (again extract without ID)
public class Note
{
public virtual Case Case {get; set;}
public virtual long CaseId {get; set;}
}
and that would be a UNIT TEST statement to load notes:
var list = NHibernateSession
.Query<Note>()
.Where(n => n.CaseId == 123)
.Skip(0).Take(10)
.ToList();
then NHibernate will NEVER load the Case object. Never. Because:
NHibernate is lazy, just live with it
The reason, the trigger to load related reference (Case property) must be something explicit.
Mostly:
there is usage of the Case object somewhere. E.g. in override of the GetHashCode() the Case.ID is used
Or:
there is a serialization or DTO conversion which does touch the Case property
In those case, NHibernate must load that...
So, create few unit tests with basic queries and assure that your the is really as shown above. Then it will work as expected

NHibernate Many-to-one cascade

I have the below two classes:
public class Project
{
public virtual int ProjectId { get; set; }
public virtual string ProjectName { get; set; }
public virtual LegalEntity LegalEntity { get; set; }
}
and
public class LegalEntity
{
public virtual int LegalEntId { get; set; }
public virtual string Name { get; set; }
}
with mappings as:
<class name="Project" table="Project" dynamic-update="true">
<id name="ProjectId">
<generator class="native"/>
</id>
<property name="ProjectName" />
<many-to-one name="LegalEntity" column="LegalEntId" fetch="join" cascade="all-delete-orphan" />
</class>
and
<class name="LegalEntity" table="LegalEnt" dynamic-update="true">
<id name="LegalEntId">
<generator class="native"/>
</id>
<property name="Name" />
</class>
In database, Project table has a FK to LegalEntity's PK column. One Project will have only one legal entity. Different projects can have same legal entity. So thats the reason I have gone for many-to-one. Not sure if this is correct though.
Insert and update is working fine. But if I update a legal entity id in a project and that legal entity becomes orphan, I want it to be deleted. But its not happening. Am I wrong in understanding delete-all-orphan? If yes, how can I achieve this behaviour?
The many-to-one cascade does not support all-delete-orphan, see:
5.1.10. many-to-one
<many-to-one
...
cascade="all|none|save-update|delete" (4)
...
Also, it would be almost impossible to handle this feature by NHibernate's session. Because it does not have to be clear, that the referenced many-to-one is really orphan. There should be some farther checks in DB... there could be other places referencing this table row...
Suggestion: do it in your code as a part of the DAO or Business Facade implementation. Check if there are really no dependencies, and then issue explicit Delete()
EXTEND: Here is a QueryOver syntax to get a list of all "orphan" LegalEntity
// subquery
var subquery = QueryOver.Of<Project>()
.Select(x => x.LegalEntity.LegalEntId);
// just these legal entities, which are NOT used
var query = session.QueryOver<LegalEntity>()
.WithSubquery
.WhereProperty(y => y.LegalEntId)
.NotIn(subquery)
;
// orphans
var list = query
.List<LegalEntity>();
Now all-delete-orphan and delete-orphan have been implemented for many-to-one as you can see in this commit from Nov 19, 2014.
Those were not supported when the OP asked the questions or when Radim Köhler wrote his answer, but I think future visitors will appretiate the update.
The documentation is also updated and now says:
cascade="all|none|save-update|delete|delete-orphan|all-delete-orphan"
But the documentation is confusing now, because it still has the following note:
The cascade attribute permits the following values: all, save-update, delete, none.
So I've created a defect to fix that last part of the documentation.

nhibernate composite-id with not existing key-many-to-one record

i have old legacy DB which has dead links in their tables. I have class mapped in nhibernate like this:
<class name="Visible" table="table_visible">
<composite-id>
<key-many-to-one column="object_id" name="ObjectA" />
<key-many-to-one column="sub_object_id" name="SubObject" />
</composite-id>
<property column="visible" name="VisibleRow" />
</class>
and:
public class Visible
{
public virtual ObjectAClass ObjectA { get; set; }
public virtual SubObjectClass SubObject { get; set; }
public virtual bool VisibleRow { get; set; }
public override bool Equals(object obj)
{
var other = ((Visible)obj);
return this.ObjectA.Equals(other.ObjectA) && this.SubObject.Equals(other.SubObject);
}
public override int GetHashCode()
{
return this.ObjectA.GetHashCode() + (this.SubObject != null? this.SubObject.GetHashCode(): 0);
}
}
Now all works fine when all joins in database are correct, but when i find such sub_object_id which doesnt have entity, nhibernate throws me error
No row with the given identifier exists:[SubObject#123]
Is there a way to map composite key so that when its subentity is not found, the whole entity wouldnt be loaded (like with inner join)?
NHibernate v2.0.50727
Following Daniel Schilling idea of fetch Visible entities with a where exists sub-query, found that there is loader element available in mappings.
<class name="ObjectA" table="table_object">
.........
<set name="VisibleList" cascade="all" lazy="false" inverse="true">
<key column="object_id" />
<one-to-many class="Visible" />
<loader query-ref="valid_entities"/>
</set>
</class>
<sql-query name="valid_entities">
<load-collection alias="v" role="ObjectA.VisibleList"/>
SELECT {v.*}
FROM table_visible v
INNER JOIN table_sub_entities e ON e.sub_entity_id=v.sub_entity_id
WHERE v.object_id=?
</sql-query>
And nothing else needed to be changed.
<key-many-to-one column="sub_object_id" name="SubObject" not-found="ignore" />
... may be helpful. From the NHibernate Documentation...
ignore will treat a missing row as a null association
Please be aware of the performance penalty associated with using this option. Whenever NHibernate fetches a Visible entity, it will also have to fetch SubObject. If you don't go ahead and fetch it in your query, this means that NHibernate will be issuing lots of lazy loads.
This doesn't meet your "when its sub-entity is not found, the whole entity wouldn't be loaded" goal. Instead NHibernate would give you an entity with a null sub-entity. If you want that inner-join-like behavior, then I think you would need to fetch your Visible entities with a where exists sub-query to make sure the SubObject actually exists.
The best option would be to fix the data in the database and add a foreign key constraint.
I just ran across this: Relations with not-found="ignore". I promise I'm not copying Ricci's content - I'm writing this from my own experience.

NHibernate creates proxy via session.Load(), but not via Linq or Criteria API

I have an odd problem in my current project. Lazy loading for queries does not work. When I query a list, nhibernate fetches all associations separately.
I extracted small parts of it and put it into a separate solution. Basically what I've got now, is a Account-Table and a AccountSync-Table. Both have an ID and a URL, while the ID is just a db-guid.
My classes are:
public class HippoAccount
{
public virtual Guid Id { get; set; }
public virtual string Url { get; set; }
public virtual HippoAccountSync Sync { get; set; }
}
public class HippoAccountSync
{
public virtual Guid Id { get; set; }
public virtual string Url { get; set; }
public virtual HippoAccount Account { get; set; }
}
When I now load a object via it's guid:
var account = session.Load<HippoAccount>(accountId);
Console.WriteLine(NHibernateUtil.IsPropertyInitialized(account, "Sync"))
... it returns false and account itself is a proxy.
But when loading a list via the criteria API:
var account = (HippoAccount)session
.CreateCriteria(typeof (HippoAccount))
.Add(Restrictions.Eq("Id", accountId))
.List()[0];
... the property Sync gets initialized (firing a second select query), and the returned object is not a proxy.
Is that default behaviour? What am I getting wrong?
The mapping is:
<class name="HippoAccount" table="AllAccounts">
<id name="Id" type="guid">
<generator class="guid"/>
</id>
<property name="Url" />
<many-to-one
class="HippoAccountSync"
name="Sync"
not-found="ignore"
property-ref="Url">
<column name="url" />
</many-to-one>
</class>
<class name="HippoAccountSync"
mutable="false"
table="Accounts">
<id name="Id" type="guid">
<generator class="guid"/>
</id>
<property name="Url">
<column name="serviceUri" />
</property>
<many-to-one class="HippoAccount"
name="Account"
property-ref="Url"
not-found="ignore">
<column name="serviceUri" />
</many-to-one>
</class>
After quite some more research, I found the answers. Answers, because there are many things that can prevent lazy loading in NHibernate.
Query vs. session.Load: When fetching an item via session.Load() you get a proxy. But as soon as you access any property, lets say the Url, the object is fetched including all it's associations that doesn't support lazy loading.
property-ref: Lazy loading only works over a objects id. When an property-association is resolved via a different column in the target entity, NH fetches it eagerly. Not that this wouldn't be possible, it's just not implemented: Bug
not-found="ignore" allows invalid foreign keys, that is, if the referenced entity isn't found NH will init the property with null. NH doesn't intercept the property-access for lazy loading, but instead assignes a object proxy. With not-found="ignore" it can't decide if the property should be set to null or a proxy for the given, possibly invalid, foreign key. This could possibly be solved by intercepting the property access.
When disabling not-found="ignore" and property-ref the schema export would generate constraints that enforce a circular reference. Not good! The correct mapping would then be a constrained one-to-one relationship, where the key for HippoAccountSync must have a generator foreign.
Resources
Select statement issued for each not-found=ignore
Lazy-load conflicts with Property-ref in Many-to-One Mapping
Google groups discussion