6

我正在尝试使用从数据库更新记录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>();

前面的语句获得了一个只有一条记录(好)的集合,name 属性是用旧值而不是“modified_name”建立的。

我该如何解决这种行为?一级缓存打扰我了?同样的问题发生在

CreateCriteria<T>();

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

此外,我在 NHibernate SQL 输出中检查了正在执行带有 WHERE 子句的选择(因此 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)]. 

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

非常感谢帮助。

4

6 回答 6

4

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

  • 将您的 Session 作为IStatelessSession, 通过调用sessionFactory.OpenStatelesSession()而不是sessionFactory.OpenSession()
  • Session.Evict(myEntity)在数据库中持久化实体后执行
  • Session.Clear()在你之前执行QueryOver
  • CacheMode您的 Session设置为Ignore, Put or Refresh在您之前QueryOver(从未测试过)

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

于 2013-04-04T08:21:28.617 回答
2

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

执行查询也不会导致第一级缓存被命中。这是因为第一级缓存仅适用于Getand Load,即如果先前已经加载了 1 的值,则将命中第一级缓存,而session.Get<MyEntity>(1)不会。MyEntityidsession.QueryOver<MyEntity>().Where(x => x.id == 1)

有关 NHibernate 缓存功能的更多信息可以在Ayende Rahien的这篇文章中找到。

总之,您有两个选择:

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

第一个选项在几乎所有情况下都是最好的。

*我知道这条规则的唯一例外是Identity用作 id 生成器类型时。

于 2013-04-04T08:42:16.660 回答
1

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

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

如果这仍然不起作用,请尝试在查询后添加:

NhibernateHelper.Session.Refresh(result);
于 2013-04-04T15:29:38.487 回答
0

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

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

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

于 2013-04-08T14:11:57.473 回答
0

这里的人不想打电话Session.Clear(),因为它太强大了。
另一方面,Session.Evict()当事先不知道对象时,可能看起来不适用。
其实还是可以用的。
您需要首先使用查询检索缓存的对象,然后调用Evict()它们。然后再次检索再次调用相同查询的新对象。
如果对象一开始没有被缓存,这种方法效率会稍低——从那时起实际上会有两个“新”查询——但对于这个缺点似乎没什么可做的......
顺便说一句,Evict()接受 null 参数也没有例外 - 如果查询的对象实际上不存在于数据库中,这很有用。

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>()
于 2016-12-30T20:38:04.570 回答
0

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

于 2017-04-20T21:32:05.440 回答