Nhibernate QueryOver不会获得最新的数据库更改

时间:2013-04-04 06:58:30

标签: nhibernate nhibernate-criteria queryover

我正在尝试使用QueryOver从数据库更新记录。 我的代码最初创建一个实体并保存在数据库中,然后相同的记录在数据库外部(从其他程序,手动或在其他机器上运行的同一程序)更新,当我调用queryOver过滤字段时更改,查询获取记录但没有最新更改。

这是我的代码:

//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>();

前一个语句只有一个记录(好),,名称属性已建立旧值而不是“modified_name”。

我如何解决此问题?一级缓存令我不安?

会出现同样的问题
CreateCriteria<T>();

我的NhibernateHelper中的会话在任何时候都没有因应用程序框架要求而关闭,只是为session.Save()关联的每个提交创建了事务。 如果我打开一个新会话来执行查询,我会从数据库中获得最新的更改,但设计要求不允许这种方法。

此外,我已经在NHibernate SQL输出中检查了正在执行带有WHERE子句的select(因此Nhibernate命中数据库)但是没有更新返回的对象!!!!

更新

以下是调用session.Save后SaveEntity中的代码:完成对Commit方法的调用

public virtual void Commit() 
{ 
  try 
  { 
    this.session.Flush(); 
    this.transaction.Commit();
  } 
  catch 
  { 
    this.transaction.Rollback(); 
    throw; 
  } 
  finally 
  { 
    this.transaction = this.session.BeginTransaction();
  } 
}

NHibernate为SaveEntity生成的SQL:

NHibernate: INSERT INTO MYCOMPANY.MYENTITY (NAME) VALUES (:p0);:p0 = 'new_name'. 

NHibernate为QueryOver生成的SQL:

NHibernate: SELECT this_.NAME as NAME26_0_ 
            FROM MYCOMPANY.MYENTITY this_ 
            WHERE this_.NAME = :p0;:p0 = 'modified_name' [Type: String (0)]. 

由于公司机密政策,查询已被修改。

非常感谢。

6 个答案:

答案 0 :(得分:4)

据我所知,您有几种选择:

  • 通过调用IStatelessSession代替sessionFactory.OpenStatelesSession()
  • 将您的会话设为sessionFactory.OpenSession()
  • 在DB
  • 中保留实体后执行Session.Evict(myEntity)
  • Session.Clear()
  • 之前执行QueryOver
  • CacheMode之前将会话的Ignore, Put or Refresh设置为QueryOver(从未测试过)

我想这个选择将取决于你长期运行会话的使用情况(恕我直言,似乎带来的问题多于解决方案)

答案 1 :(得分:1)

调用session.Save(myEntity) 不会导致更改立即持久保存到数据库* 。当框架本身或您自己调用session.Flush()时,这些更改将保持不变。有关刷新及何时调用的详细信息,请参阅this问题和nhibernate documentation有关刷新的信息。

同样执行查询不会导致第一级缓存被点击。这是因为第一级缓存仅适用于GetLoad,即如果session.Get<MyEntity>(1)的{​​{1}}为MyEntity,则id会触及第一级缓存已经加载过,而session.QueryOver<MyEntity>().Where(x => x.id == 1)则没有加载。

关于NHibernate缓存功能的更多信息可以在Ayende Rahien的this帖子中找到。

总之,您有两种选择:

  1. SaveEntity方法中使用交易,即

    using (var transaction = Helper.Session.BeginTransaction())
    {
      Helper.Session.Save(myEntity);
      transaction.Commit();
    }
    
  2. session.Flush()方法中调用SaveEntity,即

      Helper.Session.Save(myEntity);
      Helper.Session.Flush();
    
  3. 第一个选项是所有场景中的最佳选择。

    *我对此规则的唯一例外是使用Identity作为id生成器类型。

答案 2 :(得分:1)

尝试将您的上一个查询更改为:

 var result = NhibernateHelper.Session
             .QueryOver<MyEntity>()
             .CacheMode(CacheMode.Refresh)
             .Where(param => param.Name == "modified_name")

如果仍然无效,请尝试在查询后添加:

NhibernateHelper.Session.Refresh(result);

答案 3 :(得分:0)

经过搜索和搜索,思考和思考......我找到了解决方案。

修复:它包括打开一个新会话,在此会话中调用QueryOver<T>()并成功刷新数据。如果您未初始化子集合,则可以在映射中调用HibernateUtil.Initialize(entity)或设置lazy="false"。在大型集合中特别注意lazy="false",因为您可能会遇到性能不佳的问题。要解决此问题(加载大型集合的性能问题),请在集合映射中设置lazy="true",并调用受影响集合中提到的方法HibernateUtil.Initialize(entity)以从数据库中获取子记录;例如,您可以从表中获取所有记录,如果您需要访问特定实体的所有子记录,请仅为感兴趣的对象调用HibernateUtil.Initialize(collection)

注意:正如@martin ernst所说,更新问题可能是hibernate中的一个错误,我的解决方案只是一个临时修复,必须在hibernate中解决。

答案 4 :(得分:0)

此处的人不想拨打Session.Clear(),因为它太强大了。
另一方面,如果事先不知道对象,Session.Evict()似乎不适用。
实际上它仍然可用。
您需要先使用查询检索缓存的对象,然后在其上调用Evict()。然后再次检索再次调用相同查询的新对象。
这种方法在对象没有开始缓存的情况下效率稍低 - 从那时起实际上会有两个&#34;新鲜&#34;查询 - 但似乎没有太多关于这个缺点...
顺便说一句,Evict()也没有例外地接受null参数 - 如果查询对象实际上不存在于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>()

答案 5 :(得分:0)

我得到的东西非常相似,并尝试过调试NHibernate。 在我的场景中,会话在相关集合(cascade:all)中创建一个带有几个子节点的对象,然后调用ISession.Flush()。 记录将写入数据库,会话需要继续而不关闭。同时,另外两个子记录被写入数据库并提交。 一旦原始会话尝试使用带有QueryOver的{​​{1}}重新加载图形,生成的SQL语句看起来非常好,并且正确地返回行,但应该接收这些新子集的集合发现已经在会话中初始化(应该是),并且基于NH由于某种原因决定完全忽略相应的行。 我认为NH在这里做出了一个无效的假设,即如果该集合已被标记为&#34;已初始化&#34;它不需要从查询中重新加载。 如果有更熟悉NHibernate内部人员的人可能会对此感兴趣,那就太好了。