我正在尝试使用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)].
由于公司机密政策,查询已被修改。
非常感谢。
答案 0 :(得分:4)
据我所知,您有几种选择:
IStatelessSession
代替sessionFactory.OpenStatelesSession()
sessionFactory.OpenSession()
Session.Evict(myEntity)
Session.Clear()
QueryOver
CacheMode
之前将会话的Ignore, Put or Refresh
设置为QueryOver
(从未测试过)我想这个选择将取决于你长期运行会话的使用情况(恕我直言,似乎带来的问题多于解决方案)
答案 1 :(得分:1)
调用session.Save(myEntity)
不会导致更改立即持久保存到数据库* 。当框架本身或您自己调用session.Flush()
时,这些更改将保持不变。有关刷新及何时调用的详细信息,请参阅this问题和nhibernate documentation有关刷新的信息。
同样执行查询不会导致第一级缓存被点击。这是因为第一级缓存仅适用于Get
和Load
,即如果session.Get<MyEntity>(1)
的{{1}}为MyEntity
,则id
会触及第一级缓存已经加载过,而session.QueryOver<MyEntity>().Where(x => x.id == 1)
则没有加载。
关于NHibernate缓存功能的更多信息可以在Ayende Rahien的this帖子中找到。
总之,您有两种选择:
在SaveEntity
方法中使用交易,即
using (var transaction = Helper.Session.BeginTransaction())
{
Helper.Session.Save(myEntity);
transaction.Commit();
}
在session.Flush()
方法中调用SaveEntity
,即
Helper.Session.Save(myEntity);
Helper.Session.Flush();
第一个选项是所有场景中的最佳选择。
*我对此规则的唯一例外是使用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内部人员的人可能会对此感兴趣,那就太好了。