Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 434
Carlos Avatar asked Apr 04 '13 06:04

Carlos


3 Answers

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 )

like image 188
jbl Avatar answered Nov 13 '22 17:11

jbl


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:

  1. Use a transaction within the SaveEntity method, i.e.

    using (var transaction = Helper.Session.BeginTransaction())
    {
      Helper.Session.Save(myEntity);
      transaction.Commit();
    }
    
  2. 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.

like image 2
mickfold Avatar answered Nov 13 '22 15:11

mickfold


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);
like image 1
Martin Ernst Avatar answered Nov 13 '22 16:11

Martin Ernst