Getting multiple queries when transaction is committed, why? - nhibernate

public Parent GetByName(string Name)
{
return _session.CreateCriteria<Parent>()
.Add(Restrictions.Eq("Name", Name))
.SetFetchMode("Children", FetchMode.Eager)
.SetResultTransformer(new DistinctRootEntityResultTransformer())
.UniqueResult<Parent>();
}
public ParentDetailVM GetMeAParent(string Name)
{
Parent parent;
using (var tx = _session.BeginTransaction())
{
//This works well, one single query loading
//both parent and children
parent = _parentRepository.GetByName(Name);
//If I include this as suggested by NHProfiler
//it all of the sudden sends a new query for each child
//and a query for the grandchildren collection
tx.Commit();
}
return Mapper.Map<Parent, ParentDetailVM>(parent);
}
I have checked to make sure that nothing in the mapping files has been set to eager load. I can't figure out why it works if I leave off the transaction commit but otherwise it issues N more queries. Anyone know why this might be happening?

If you examine _session.IsDirty() before committing the transaction my bet is that it will return true. When the transaction is committed the session is flushed and, for some reason, the child objects are loaded to cascade the change.
This is a problem known as "ghosting" or phantom updates. A typical scenario is that a database column is a nullable int but the corresponding object property is non-nullable. When a record with a null value is retrieved, NHibernate sets the property value to 0 and the object is therefore dirty.
The quickest way to troubleshoot this is to turn on dynamic-update for the object using dynamic-update="true" in XML mappings, DynamicUpdate() in fluent, and use a profiler or logging to see which columns are being updated after the select. There's also a utility called Ghostbuster that you can include in unit tests.

Related

Coldfusion - ORM removing child objects

I'm trying to nail down the specifics of removing a child in an one-to-many relationship in CF using ORM. I've posted a small test app here:
https://github.com/pnahtanoj/cfrelationship
Regarding the removeChildren() function on ln47 of create.cfm - if I dump the parent object before and after, I see that the children have been removed. However, they are still in the DB after the close of the transaction. Not sure what I'm missing.
Using CF10, MySql 5.something...
Because you set inverse to true on the many side, that means only the child side is tracked by Hibernate.
You can set all the child's parent to null.
public void function removeChildren() {
transaction {
var children = getChilds();
for (var c in children)
c.setParent( javacast('null','') );
}
arrayClear(variables.childs);
}

Update command in Fluent NHibernate

I am trying to update an object after retrieving it from a database.
This fires 2 queries , one for the select and the other for the update, is there any way of update an object using Fluent NHiberNate firing only one query ?
My code is as below:
var userProfile = userProfileRepository
.Find(x => x.ClientId == clientId)
.FirstOrDefault();
/* update UserProfile object here */
userProfileRepository.SaveOrUpdate(userProfile);
the SaveOrUpdate Method looks as such :
public bool SaveOrUpdate(T instance)
{
using (var session = SessionManager.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
session.SaveOrUpdate(instance);
transaction.Commit();
}
return true;
}
}
In case that your issue is:
regardless of what I do, SaveOrUpdate() always sends SELECT then UPDATE
You should check the doc:
5.1.4.7. Assigned Identifiers
If you want the application to assign identifiers (as opposed to having NHibernate generate them), you may use the assigned generator. This special generator will use the identifier value already assigned to the object's identifier property. Be very careful when using this feature to assign keys with business meaning (almost always a terrible design decision).
Due to its inherent nature, entities that use this generator cannot be saved via the ISession's SaveOrUpdate() method. Instead you have to explicitly specify to NHibernate if the object should be saved or updated by calling either the Save() or Update() method of the ISession.
So, if your Fluent configuration sets the ID to be assigned - NHibernate has no other way then check if it
exists
or is new
because used method was SaveOrUpdate()
Solution(s)
1) Change the ID to be generated by DB or NHiberante 2) use explicit Update()
Are you trying to create an
UPDATE ... WHERE ...
statement?
AFAIK the NHibernate way to do this, is to select the appropriate objects (using the WHERE clause), update the fields, and persist them again.
var tempObjects = _session.Query<myObject>.Where(o => o.Id > 500);
// update proxy objects
foreach (var o in tempObjects)
{
o.MyValue = updatedValue;
}
// commit updated objects
_session.Update(tempObjects);
To be honest, we've used ISession.CreateSQLQuery ourselves. I hate using SQL in code because it breaks in refactoring, but if you must - here's how:
_session.CreateSQLQuery(
#"UPDATE [MyTable] SET [MyValue]=:updatedvalue WHERE Id > 500")
.SetParameter("updatedvalue", updatedValue)
.ExecuteUpdate();

Nhibernate QueryOver don't get latest database changes

I am trying get a record updated from database with QueryOver.
My code initially creates an entity and saves in database, then the same record is updated on database externally( from other program, manually or the same program running in other machine), and when I call queryOver filtering by the field changed, the query gets the record but without latest changes.
This is my code:
//create the entity and save in database
MyEntity myEntity = CreateDummyEntity();
myEntity.Name = "new_name";
MyService.SaveEntity(myEntity);
// now the entity is updated externally changing the name property with the
// "modified_name" value (for example manually in TOAD, SQL Server,etc..)
//get the entity with QueryOver
var result = NhibernateHelper.Session
.QueryOver<MyEntity>()
.Where(param => param.Name == "modified_name")
.List<T>();
The previous statement gets a collection with only one record(good), BUT with the name property established with the old value instead of "modified_name".
How I can fix this behaviour? First Level cache is disturbing me? The same problem occurs with
CreateCriteria<T>();
The session in my NhibernateHelper is not being closed in any moment due application framework requirements, only are created transactions for each commit associated to a session.Save().
If I open a new session to execute the query evidently I get the latest changes from database, but this approach is not allowed by design requirement.
Also I have checked in the NHibernate SQL output that a select with a WHERE clause is being executed (therefore Nhibernate hits the database) but don´t updates the returned object!!!!
UPDATE
Here's the code in SaveEntity after to call session.Save: A call to Commit method is done
public virtual void Commit()
{
try
{
this.session.Flush();
this.transaction.Commit();
}
catch
{
this.transaction.Rollback();
throw;
}
finally
{
this.transaction = this.session.BeginTransaction();
}
}
The SQL generated by NHibernate for SaveEntity:
NHibernate: INSERT INTO MYCOMPANY.MYENTITY (NAME) VALUES (:p0);:p0 = 'new_name'.
The SQL generated by NHibernate for QueryOver:
NHibernate: SELECT this_.NAME as NAME26_0_
FROM MYCOMPANY.MYENTITY this_
WHERE this_.NAME = :p0;:p0 = 'modified_name' [Type: String (0)].
Queries has been modified due to company confidential policies.
Help very appreciated.
As far as I know, you have several options :
have your Session as a IStatelessSession, by calling sessionFactory.OpenStatelesSession() instead of sessionFactory.OpenSession()
perform Session.Evict(myEntity) after persisting an entity in DB
perform Session.Clear() before your QueryOver
set the CacheMode of your Session to Ignore, Put or Refresh before your QueryOver (never tested that)
I guess the choice will depend on the usage you have of your long running sessions ( which, IMHO, seem to bring more problems than solutions )
Calling session.Save(myEntity) does not cause the changes to be persisted to the DB immediately*. These changes are persisted when session.Flush() is called either by the framework itself or by yourself. More information about flushing and when it is invoked can be found on this question and the nhibernate documentation about flushing.
Also performing a query will not cause the first level cache to be hit. This is because the first level cache only works with Get and Load, i.e. session.Get<MyEntity>(1) would hit the first level cache if MyEntity with an id of 1 had already been previously loaded, whereas session.QueryOver<MyEntity>().Where(x => x.id == 1) would not.
Further information about NHibernate's caching functionality can be found in this post by Ayende Rahien.
In summary you have two options:
Use a transaction within the SaveEntity method, i.e.
using (var transaction = Helper.Session.BeginTransaction())
{
Helper.Session.Save(myEntity);
transaction.Commit();
}
Call session.Flush() within the SaveEntity method, i.e.
Helper.Session.Save(myEntity);
Helper.Session.Flush();
The first option is the best in pretty much all scenarios.
*The only exception I know to this rule is when using Identity as the id generator type.
try changing your last query to:
var result = NhibernateHelper.Session
.QueryOver<MyEntity>()
.CacheMode(CacheMode.Refresh)
.Where(param => param.Name == "modified_name")
if that still doesn't work, try add this after the query:
NhibernateHelper.Session.Refresh(result);
After search and search and think and think.... I´ve found the solution.
The fix: It consist in open a new session, call QueryOver<T>() in this session and the data is succesfully refreshed. If you get child collections not initialized you can call HibernateUtil.Initialize(entity) or sets lazy="false" in your mappings. Take special care about lazy="false" in large collections, because you can get a poor performance. To fix this problem(performance problem loading large collections), set lazy="true" in your collection mappings and call the mentioned method HibernateUtil.Initialize(entity) of the affected collection to get child records from database; for example, you can get all records from a table, and if you need access to all child records of a specific entity, call HibernateUtil.Initialize(collection) only for the interested objects.
Note: as #martin ernst says, the update problem can be a bug in hibernate and my solution is only a temporal fix, and must be solved in hibernate.
People here do not want to call Session.Clear() since it is too strong.
On the other hand, Session.Evict() may seem un-applicable when the objects are not known beforehand.
Actually it is still usable.
You need to first retrieve the cached objects using the query, then call Evict() on them. And then again retrieve fresh objects calling the same query again.
This approach is slightly inefficient in case the object was not cached to begin with - since then there would be actually two "fresh" queries - but there seems to be not much to do about that shortcoming...
By the way, Evict() accepts null argument too without exceptions - this is useful in case the queried object is actually not present in the DB.
var cachedObjects = NhibernateHelper.Session
.QueryOver<MyEntity>()
.Where(param => param.Name == "modified_name")
.List<T>();
foreach (var obj in cachedObjects)
NhibernateHelper.Session.Evict(obj);
var freshObjects = NhibernateHelper.Session
.QueryOver<MyEntity>()
.Where(param => param.Name == "modified_name")
.List<T>()
I'm getting something very similar, and have tried debugging NHibernate.
In my scenario, the session creates an object with a couple children in a related collection (cascade:all), and then calls ISession.Flush().
The records are written into the DB, and the session needs to continue without closing. Meanwhile, another two child records are written into the DB and committed.
Once the original session then attempts to re-load the graph using QueryOver with JoinAlias, the SQL statement generated looks perfectly fine, and the rows are being returned correctly, however the collection that should receive these new children is found to have already been initialized within the session (as it should be), and based on that NH decides for some reason to completely ignore the respective rows.
I think NH makes an invalid assumption here that if the collection is already marked "Initialized" it does not need to be re-loaded from the query.
It would be great if someone more familiar with NHibernate internals could chime in on this.

cached collection not being invalidated by Nhibernate

I have two objects - ContentPage, which has a collection of ChildLinks.
ContentPage
-----------
ID
Title
ChildLink
----------
ID
ParentPageID [ContentPage]
ChildPageID [ContentPage]
Priority
The ContentPage.ChildLinks property utilises the 2nd level cache. I am using Fluent NH to configure Nhibernate, and using Nhibernate 3.1. Cache is set as 'Read-Write' both for the collection, and the 'ChildLink' class.
I've noticed that whenever I delete a ChildLink, the collection cache is not being invalidated. Thus, when I call the ContentPage.ChildLinks, I get an error:
no row with the given identifier exists
I've turned off the cache, and it works well. Shouldn't the cache be automatically invalidated? I am using SysCache as the cache provider, and MySQL as the database.
Thanks in advance!
I had the same problem and I can across the following article which solved my problem:
Inverse Mapped Collections and NHibernate's Second-Level Cache
Basically if you have mapped your collections as inverse, when you delete the child item you also have to make sure to explicitly remove it from the parent collection or the cache state will be invalid after you delete the child. The first thing to check is if the relationship really needs to be inverse.
Assuming inverse is necessary or desired, and using your example:
Instead of only something like:
Session.Delete(ChildLink);
You have to do:
ContentPage.ChildLinks.Remove(ChildLink);
ChildLink.ParentPage = null;
Session.Delete(ChildLink);
You also might need to explicitly save your ContentPage object at this point as well, it depends on your Session flush settings.
I use methods on my entities for managing such inverse relationships, for example:
public ChildLink
{
public ContentPage ParentPage {get;set;}
public void AddToPage(ContentPage addTo)
{
addTo.ChildLinks.Add(this);
this.ParentPage = addTo;
}
public void RemoveFromPage()
{
ParentPage.ChildLinks.Remove(this);
this.ParentPage = null;
}
}
And then when deleting a child object:
ChildLink.RemoveFromPage();
Session.Delete(ChildLink);

Best way to delete all rows in a table using NHibernate?

To keep my integration tests independent I remove all old data and insert new test data before each test. Is there a better way of doing this than simply querying for all entities and deleting them one by one?
I have considered writing a stored proc that runs "delete from tablename;" for each table that is to be cleared. That ought to quite a bit faster, but it would be nice to do it without doing SQL queries or calling SPs via NH.
I'm using vanilla NHibernate and Linq to NHibernate. I beleive Castle Active Record has something like Foo.DeleteAll(), but I don't want to use Active Record for this project.
Any ideas?
Thanks /Erik
UPDATE:
Since this question was asked and answered, progress has been made by the NHibernate team. As Ayende explains in this blog post, you can now execute DML queries directly, without NHibernate having to fetch any entities.
To delete all Foo objects you could do like this:
using (ISession session = ...)
using (ITransaction transaction = session.BeginTransaction())
{
session.CreateQuery("delete Foo f").ExecuteUpdate();
transaction.Commit();
}
This query would generate the following SQL:
delete from Foo
which aught to be significantly faster than fetching the entities first and then deleting them. Be careful though, since queries like these do not affect the level 1 cache.
In the TearDown of my UnitTests, I mostly do this:
using( ISession s = ... )
{
s.Delete ("from Object o");
s.Flush();
}
This should delete all entities.
If you want to delete all instances of one specific entity, you can do this:
using( ISession s = .... )
{
s.Delete ("from MyEntityName e");
s.Flush();
}
Offcourse, there's a drawback with this method, and that is that NHibernate will first fetch the entities before deleting them.
I use Fluent Nhibernate attributes so I modify code a little in order not to hardcore table names
private static void CleanUpTable<T>(ISessionFactory sessionFactory)
{
var metadata = sessionFactory.GetClassMetadata(typeof(T)) as NHibernate.Persister.Entity.AbstractEntityPersister;
string table = metadata.TableName;
using (ISession session = sessionFactory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
string deleteAll = string.Format("DELETE FROM \"{0}\"", table);
session.CreateSQLQuery(deleteAll).ExecuteUpdate();
transaction.Commit();
}
}
}
usage
CleanUpTable<Person>(sessionFactory);
With NHibernate 5.0 you can now simply do:
session.Query<Foo>().Delete();
Documentation:
//
// Summary:
// Delete all entities selected by the specified query. The delete operation is
// performed in the database without reading the entities out of it.
//
// Parameters:
// source:
// The query matching the entities to delete.
//
// Type parameters:
// TSource:
// The type of the elements of source.
//
// Returns:
// The number of deleted entities.
public static int Delete<TSource>(this IQueryable<TSource> source);