我正在尝试使用 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>();
前一个语句获取的集合只有一个记录(好), BUT ,名称属性已 Build with the old value 而不是"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 回答
据我所知,你有几个选择:
将您的会话设为
IStatelessSession
,方法是调用sessionFactory.OpenStatelesSession()
而不是sessionFactory.OpenSession()
在DB中持久保存实体后执行
Session.Evict(myEntity)
在
QueryOver
之前执行Session.Clear()
在你的
QueryOver
之前将你的Session的CacheMode
设置为Ignore, Put or Refresh
(从未测试过)我想这个选择将取决于你长期运行会话的使用情况(恕我直言,似乎带来的问题多于解决方案)
致电
session.Save(myEntity)
does not cause the changes to be persisted to the DB immediately* . 当框架本身或您自己调用session.Flush()
时,这些更改将保持不变 . 有关刷新及何时调用的更多信息,请参见this问题和nhibernate documentation关于刷新 .也 performing a query will not cause the first level cache to be hit . 这是因为第一级缓存仅适用于
Get
和Load
,即session.Get<MyEntity>(1)
将在第一级缓存中命中,如果MyEntity
的id
为1,之前已经加载过,而session.QueryOver<MyEntity>().Where(x => x.id == 1)
则不会 .有关NHibernate缓存功能的更多信息可以在Ayende Rahien的文章中找到 .
总之,您有两个选择:
SaveEntity
方法中使用交易,即SaveEntity
方法中调用session.Flush()
,即第一个选项是几乎所有 all 场景中的最佳选择 .
*我对此规则的唯一例外是使用
Identity
作为id生成器类型 .尝试将您的上一个查询更改为:
如果仍然无效,请尝试在查询后添加:
经过搜索和搜索,思考和思考......我找到了解决方案 .
修复:它包括打开一个新会话,在此会话中调用
QueryOver<T>()
并成功刷新数据 . 如果未初始化子集合,则可以在映射中调用HibernateUtil.Initialize(entity)
或设置lazy="false"
. 在大型系列中特别注意lazy="false"
,因为你的表现可能会很糟糕 . 要解决此问题(加载大型集合的性能问题),请在集合映射中设置lazy="true"
并调用受影响集合的上述方法HibernateUtil.Initialize(entity)
以从数据库中获取子记录;例如,您可以从表中获取所有记录,如果您需要访问特定实体的所有子记录,请仅为感兴趣的对象调用HibernateUtil.Initialize(collection)
.注意:正如@martin ernst所说,更新问题可能是hibernate中的一个错误,我的解决方案只是一个临时修复,必须在hibernate中解决 .
这里的人不想打电话
Session.Clear()
,因为它太强大了 .另一方面,当事先不知道对象时,
Session.Evict()
似乎不适用 .实际上它仍然可用 .
您需要首先使用查询检索缓存的对象,然后对它们调用
Evict()
. 然后再次检索再次调用相同查询的新对象 . 如果对象没有开始缓存,这种方法效率稍低 - 从那时起实际上会有两个"fresh"查询 - 但似乎没有太多关于这个缺点的事情......顺便说一下,
Evict()
也没有异常地接受null参数 - 这在查询对象实际上不存在于DB中时很有用 .我得到的东西非常相似,并尝试调试NHibernate . 在我的场景中,会话在相关集合(cascade:all)中创建一个具有几个子节点的对象,然后调用
ISession.Flush()
. 记录将写入数据库,会话需要继续而不关闭 . 同时,另外两个子记录被写入数据库并提交 . 一旦原始会话然后尝试使用带有JoinAlias
的QueryOver
重新加载图形,生成的SQL语句看起来非常好,并且正确地返回行,但是发现应该接收这些新子集的集合已经在其中初始化会话(应该是),并且基于NH由于某种原因决定完全忽略各自的行 . 我认为NH在这里做了一个无效的假设,如果集合已经标记为"Initialized",则不需要从查询中重新加载 . 如果有更熟悉NHibernate内部的人可能会对此感兴趣,那就太好了 .