Seeing a scenario where upon loading an entity by id, NH is loading via proxy (which is expected), however, backing fields for id = 0, but virtual property (getter), is the id of the entity from the db.
Example:
private int _id;
public virtual int Id { get { return _id; } }
and mapping
...
<id name="Id" access="nosetter.camelcase-underscore">
...
Upon loading the entity, the _id = 0, but Id = 4 for example.
Is it normal behaviour for the backing fields to not be initialised while entity is proxied?
I would say: do not worry. There is nothing wrong. And it is normal behaviour.
You can try to create another property, e.g.
public virtual int MyTestId
{
get { return _id + 1; }
}
And you'll see that MyTestId is 5 (to continue the case study above, when the Id == 4). Simply, you've most likely faced the VS debugger... which is just a human.
I have an entity A with a bool property (lets call it BProp).
In a second entity there is a bag mapping of A elements with a where condition on BProp like this:
<bag name="MyBag" cascade="all-delete-orphan" inverse="false" where="BProp = 1">
<key column="Structure_Id" />
<one-to-many entity-name="A" />
</bag>
The problem is tha BProp = 1 works with SqlServer and Oracle but breaks PostgreSQL which needs a condition like
where="BProp = true"
Is there a clever way to create a single hbm.xml mapping for all the three db I have to suppport?
As the query substition does not seem to be a usable solution in this filter case, you may go for query interceptor (here a quite rough implementation) :
public class BooleanInterceptor : EmptyInterceptor, IInterceptor
{
public string TrueToken { get; set; }
public string FalseToken { get; set; }
NHibernate.SqlCommand.SqlString IInterceptor.OnPrepareStatement(NHibernate.SqlCommand.SqlString sql)
{
return sql.Replace("{TrueToken}", TrueToken).Replace("{FalseToken}", FalseToken);
}
}
Your mapping would then be where="BProp = {TrueToken}"
It would be used like this
var interceptor = new BooleanInterceptor ();
using (var session = sessionFactory.OpenSession(interceptor))
{
interceptor.FalseToken = "0"; // this replacement value should be taken from a config file
interceptor.TrueToken = "1"; // see above
// your code here
session.Close();
}
I bet there are way better solutions, but I hope this will help anyway
-------------------------- previous answer
I use to insert query substitutions in my NHibernate configuration, like this
var cfg = new Configuration();
cfg.Properties.Add("dialect", "NHibernate.Dialect.MsSql2005Dialect");
.
cfg.Properties.Add("query.substitutions", "true 1, false 0");
.
I guess you could have your xml mapping with where="BProp = true" and just insert the cfg property for query.substitions depending if the dialect is MsSql / Oracle or PosgresSQL
Just a quickie ... I have the following ID generator strategy for one of my mapped classes:
<id name="UID" type="System.Guid">
<column name ="UID" sql-type ="uniqueidentifier" />
<generator class="guid.comb" />
</id>
The entity in question is involved in synchronisation / merging behaviours from which it is necessary to have a globally unique identifier.
When an entity is created on a client application for the first time, it's UID property gets assigned so that it is the same value of the equivilent entity on the server.
However the above ID generator strategy overwrites any value provided for new/transient entities.
Whats the fix? Will I have to remove the generator strategy and assign my own GUIDs? Or is the generator strategy configurable to only generate a guid.comb when required?
I think you can accomplish this by making UID a private field and controlling access through the property.
public class MyClass
{
private Guid _uid;
protected MyClass() { // parameterless ctor for NH }
public MyClass(Guid uid) { _uid = uid; // assign on creation }
public Guid Uid
{
get { return _uid; }
private set { // do nothing or compare _uid to Guid.Empty and set }
}
}
Can someone explain this little mystery to me about how NHibernate handles composite elements.
I have classes that look like this;
public class Blog
{
public virtual int Id
{
get;
private set;
}
public virtual ISet<Comment> Comments
{
get;
set;
}
}
public class Comment
{
public virtual string CommentText
{
get;
set;
}
public virtual DateTime Date
{
get;
set;
}
}
and mappings like this;
<class name="Blog" table="blog">
<id name="Id" column="id" unsaved-value="0">
<generator class="hilo"/>
</id>
<set name="Comments" table="blog_comments">
<key column="blog_id" />
<composite-element class="Comment">
<property name="CommentText" column="comment" not-null="true" />
<property name="Date" column="date" not-null="true" />
</composite-element>
</set>
</class>
However when i perform a select like this;
using (ITransaction transaction = session.BeginTransaction())
{
Blog blog = session.CreateCriteria(typeof(Blog))
.SetFetchMode("Comments", FetchMode.Eager)
.Add(Expression.IdEq(2345))
.UniqueResult();
transaction.Commit();
}
NHibernate issues a select with a join to get the blog with posts BUT then deletes all comments and then inserts the comments! Why is it doing this? If i do not use a transaction then it will ONLY perform the select and not the DELETE and INSERT as I would expect. What am I missing? I am using NHibernate 2.0
I think you need to override Equals() and GetHashCode() on Comment. NHibernate doesn't have an ID to go on for entity equality so you have to define what makes a comment entity equal to another comment.
Could be wrong :)
Edit
From nhibernate.info (8.2)
Note: if you define an ISet of composite elements, it is very important to implement Equals() and GetHashCode() correctly.
And an example of implementing Equals / GetHashCode from nhibernate.info (4.3)
public class Cat
{
...
public override bool Equals(object other)
{
if (this == other) return true;
Cat cat = other as Cat;
if (cat == null) return false; // null or not a cat
if (Name != cat.Name) return false;
if (!Birthday.Equals(cat.Birthday)) return false;
return true;
}
public override int GetHashCode()
{
unchecked
{
int result;
result = Name.GetHashCode();
result = 29 * result + Birthday.GetHashCode();
return result;
}
}
}
My question would be why you are committing if you only need to do a select? I believe the reason it's deleting all the comments is that when you call commit on the transaction, the blog object and it's associated comments are cached in the session that is used to create the transaction. When you call the commit, you are causing all the objects in the session to be saved which is causing the save back to the database. I'm not clear on why it's deleting the comments but it is correct behaviour to save the objects.
I also stumbled upon this today:
NHibernate is deleting my entire
collection and recreating it instead
of updating the table.
This generally happens when NHibernate
can't figure out which items changed
in the collection. Common causes are:
replacing a persistent collection entirely with a new collection instance
passing NHibernate a manually constructed object and calling Update on it.
serializing/deserializing a persistent collection apparently also causes this problem.
updating a with inverse="false" - in this case, NHibernate can't construct SQL to update an individual collection item.
Thus, to avoid the problem:
pass the same collection instance that you got from NHibernate back to it (not necessarily in the same session),
try using some other collection instead of ( or ), or
try using inverse="true" attribute for .
I previously asked a question regarding modeling of a situation with Users, Items, and UserRatings. In my example UserRatings are associated with one User and one Item. A good answer was provided by Nathan Fisher and I've included the model he suggested below.
But I now have a question regarding retrieval of these objects.
The model links the entities by holding references to the entities.My question is, how best do I retrieve a particular UserRating to be updated? In this situation I would have the userID (from the asp.net auth session), and the itemID (from the URL). Also, there could be 1000s of ratings per user or item.
Back in the old school this would be as simple as one update query 'where x = userID and y=itemID. Easy. However the best way to accomplish this in NHibernate using a proper object model is not so clear.
A) I understand that I could create a repository method GetRatingByUserAndItem, and pass it both a User and Item object, which it would do an HQL/criteria query on to retrieve the Rating object. However to do this I assume that I would first need to retrieve User and the Item from the ORM before passing these back to the ORM in the query. I would then get the UserRating object, update it, and then have the ORM persist the changes. This seems ridiculously inefficent to me, compared to the old school method.
B) Maybe I could just new-up the UserRating object, and do a createorupdate type call the ORM (not sure on exact syntax). This would be better, but presumably I would still need to first retrieve the User and Item, which is still pretty inefficient.
C) Perhaps I should just retrieve the User (or the Item) from the ORM, and find the correct UserRating from its UserRatings collection. However, if I do that, how do I make sure that I'm not retrieving all of the UserRatings related to that User (or Item), but just the one related to the specific item and specific user?
D) It occured to me that I could just drop the full-blown references to User and Item from UserRating in the model, and instead have primitive references (UserID and ItemID). This would allow me to do something as simple as the oldschool method. Very tempting, but this just doesn't seem right to me - not very Object Oriented (and surely that's the main reason we are using an ORM in the first place!)
So, can anyone offer some sage advice? Am I on the right track with any of the options above? Or is there a better way that I have not considered?
Thanks in advace for your help! :)
UPDATE:
I've just posted a bounty for this, and understand this better, I would also like to know, using a similar approach, how best to perform the following queries:
Retrieve all the Items which a user had NOT rated.
Retrieve the Item(s) and Item rating(s) which the user had rated the lowest.
The Model follows below:
public class User
{
public virtual int UserId { get; set; }
public virtual string UserName { get; set; }
public virtual IList<UserRating> Ratings { get; set; }
}
public class Item
{
public virtual int ItemId { get; set; }
public virtual string ItemName { get; set; }
public virtual IList<UserRating> Ratings { get; set; }
}
public class UserRating
{
public virtual User User { get; set; }
public virtual Item Item { get; set; }
public virtual Int32 Rating { get; set; }
}
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Test" namespace="Test" >
<class name="User">
<id name="UserId" >
<generator class="native" />
</id>
<property name="UserName" />
<bag name="Ratings" generic="true" inverse="true" table="UserRating">
<key column="UserId" />
<one-to-many class="UserRating"/>
</bag>
</class>
<class name="Item" >
<id name="ItemId" >
<generator class="native" />
</id>
<property name="ItemName" />
<bag name="Ratings" generic="true" inverse="true" table="UserRating">
<key column="ItemId" />
<one-to-many class="UserRating"/>
</bag>
</class>
<class name="UserRating" >
<composite-id>
<key-many-to-one class="User" column="UserId" name="User" />
<key-many-to-one class="Item" column="ItemId" name="Item" />
</composite-id>
<property name="Rating" />
</class>
</hibernate-mapping>
Normally, when you use an ORM, you want to implement the business logic (say: changing data) object oriented. This requires to load the objects from the database. NH allows you to load them once, and change it without any reference to any database related stuff, but just changing property values.
This said, it is not always as easy as that. Sometimes there are performance reasons which requires other ways to update data.
You could use HQL updates or even SQL updates.
Another, more classical way to accomplish this is to only load UserRatings. This requires to make it an independent entity (it needs an id, avoid the composite id anyway, replcae it with many-to-one references). Then you filter the UserRatings by user and item, load the items you want to change in the database, and change them using object oriented programming.
It is always a trade-off between performance and object oriented programming. You should try to make it as OO as possible, and only do optimizations if it is needed. Maintainability is important.
I would avoid moving foreign keys to the domain model.
I would choose option C. Your concerns about performance indicate you may be optimizing prematurely. I think it would be fine for you to have a GetUser(int userId) method, then look for the appropriate item in its Ratings collection, and update it.
This does, however, bring up a common problem that ORMs suffer called the N+1 SELECT problem. Looking at each UserRating to find the appropriate one would likely result in one SELECT statement per UserRating. There are several ways to address this. One being to change your mapping file to either disable lazy loading of the Ratings collection, or else load it using 'join' fetching - see this section of the NHibernate documentation.
Using HQL your query would look like this
Select From UserRating
Where ItemId=: #ItemId
and UserId=: #UserId
This will give you the UserRating object that you are after then you can updated in and save it as necessary
And alternative would be
Session.CreateCriteria(typeof(ClassLibrary1.UserRating))
.Add(Expression.Sql(String.Format("ItemId={0}",UserId)))
.Add(Expression.Sql(String.Format("UserId={0}",ItemId)))
.List<ClassLibrary1.UserRating>();
This was the simplest way I could get this to work. I am not happy with the embedded strings but it works.
public void UpdateRating( int userId, int itemId, int newRating, ISession session )
{
using( var tx = session.BeginTransaction())
{
var ratingCriteria = session.CreateCriteria<UserRating>()
.CreateAlias( "Item" "item" )
.CreateAlias( "User" "user" )
.Add( Restrictions.Eq( "item.ItemId", itemId ) )
.Add( Restrictions.Eq( "user.UserId", userId ) );
var userRating = ratingCriteria.UniqueResult<UserRating>();
userRating.Rating = newRating;
tx.Commit();
}
}
You will need to test this as it has been a while since I used the criteria api but essentially what this does is creates alias for two association paths and then using those aliases, adds restrictions to the User and Item so that you only get the UserRating you are interested in.
Everything is done inside a transaction and by relying on NHibernate's dirty-state tracking, the change will be flushed to the database when the transaction commits.
Depending on the version of NHibernate you are using, you could also query using NHLinq or the NHLambda stuff that has been integrated and is now accessible via session.QueryOver<T>.
To retrieve a list of all the items that a user has not rated, you will need to use a subquery to identify all of the items the user has rated and then apply a not in clause to all of the items ( essentially get me all the items not in the items the user has rated ).
var ratedItemsCriteria = DetachedCriteria.For<UserRating>()
.CreateAlias( "Item" "item" )
.SetProjection( Projections.Property( "item.ItemId" ) )
.CreateCriteria( "User" )
.Add( Restrictions.Eq( "UserId", userId ) );
var unratedItemsCriteria = session.CreateCriteria<Item>()
.Add( Subqueries.PropertyNotIn( "ItemId", ratedItemsCriteria ) );
var unratedItems - unratedItemsCriteria.List<Item>();
In general, I think most of your problems could be resolved by judicious application of google, nhforge and the nhibernate user mailing list.
Query the database (using HQL, Criteria, SQL query, etc.) for the UserRating you want to modify
Change the UserRating however you like
Commit your changes
In pseudocode, this would look something like this:
using (ITransaction transaction = session.BeginTransaction())
{
UserRating userRating = userRatingRepository.GetUserRating(userId, itemId);
userRating.Rating = 5;
transaction.Commit();
}
This will involve two queries (as opposed to the one query "old school" solution). The first query (which happens in the GetUserRating call) will run a SQL "SELECT" to grab the UserRating from the database. The second query (which happens on transaction.Commit) will update the UserRating in the database.
GetUserRating (using Criteria) would look something like this:
public IList<UserRating> GetUserRating(int userId, int itemId)
{
session.CreateCriteria(typeof (UserRating))
.Add(Expression.Eq("UserId", userId))
.Add(Expression.Eq("ItemId", itemId))
.List<UserRating>();
}
I see that this Q has not been marked as answered, so I'll give it a shot. In my opinion, you have to look a lot at how the objects are used. It seems to me that you'd relate a UserRating more to an Item than to a user, simply because you'd display it next to the item in a UI. It doesn't feel that important to always display it for a user.
That is why I would remove the list of ratings from the user:
public class User
{
public virtual int UserId { get; set; }
public virtual string UserName { get; set; }
}
If you want the ratings for a user, you'd get that through a repository.
I'd leave the Item class unchanged, since you always want to see the ratings with an item:
public class Item
{
public virtual int ItemId { get; set; }
public virtual string ItemName { get; set; }
public virtual IList<UserRating> Ratings { get; set; }
}
The UserRating class could be completely disconnected from the Item and User classes. Just keep the ids in there, so you could retrieve an Item or User from a repository if you need to:
public class UserRating
{
public virtual int UserId { get; set; }
public virtual int ItemId { get; set; }
public virtual Int32 Rating { get; set; }
}