我有一个父母子女关系,父母没有引用孩子 - 如下所示:
class Child
{
int Id { get; set;
Parent Parent { get; set; }
// + other stuff
}
class Parent
{
int Id { get; set; }
// + other stuff
}
在数据库中,这只意味着Child
表有一个ParentID
列。此列可以为空 - 即可以Parent
- 少Child
。
我使用Fluent惯例来映射域名,但我们可以覆盖此关系,以便我们可以同时更新Child
并创建新的Parent
时间:
public class ChildOverride : IAutoMappingOverride<Child>
{
public void Override(AutoMapping<Child> mapping)
{
mapping.References(x => x.Parent).Cascade.All();
}
}
当我们向现有Parent
添加新的Child
并立即需要读取该父级的ID(在交易中)时,问题就出现了,例如, :
existingChild.CreateParent(parameters);
session.Save(existingChild);
Debug.WriteLine(existingChild.Parent.Id);
只打印0
而不是给我新父母的ID - 我假设Cascade.All()
会......好吧,级联。这是错的吗?
如果我在访问Id之前提交了事务,一切都很好,或者如果我明确地保存了新的Parent也可以,例如。
// This works
existingChild.CreateParent(parameters);
session.Save(existingChild);
transaction.Commit();
Debug.WriteLine(existingChild.Parent.Id);
// This also works
existingChild.CreateParent(parameters);
session.Save(existingChild);
session.Save(existingChild.Parent);
Debug.WriteLine(existingChild.Parent.Id);
有什么方法可以更改我的覆盖,以便保存子对象将允许父ID立即可访问? (或者还有别的我做错了吗?)
答案 0 :(得分:2)
好处是,一切正常。 您的映射是正确的。因为如果这个片段正在运行......
// This works
existingChild.CreateParent(parameters);
session.Save(existingChild);
transaction.Commit();
......这个概念也在发挥作用。为什么?什么都有关系?为什么Commit()
有助于解决这个问题?
好吧,因为 ISession
是抽象,所以它是虚拟持久性存储。在我们调用session.Save()
时,不执行任何SQL语句。它只保留所有信息(在使用该会话期间收集)并仅执行SQL WRITE语句
但更好更精确的是引用doc:
ISession将不时执行将ADO.NET连接状态与内存中保存的对象状态同步所需的SQL语句。默认情况下,此过程刷新,发生在以下几点
...
除非明确
Flush()
,否则绝对无法保证Session何时执行ADO.NET调用,只保证执行它们的顺序。 然而,NHibernate确实保证ISession.Find(..)方法永远不会返回过时的数据; 也不会返回错误的数据。可以更改默认行为,以便更频繁地进行刷新。 FlushMode 类定义了三种不同的模式:仅在提交时刷新(并且仅在使用NHibernate ITransaction API时),使用解释的例程自动刷新(仅在明确的NHibernate ITransaction中工作),或者除非明确调用Flush(),否则永远不会刷新最后一种模式对于长时间运行的工作单元很有用,其中ISession保持打开状态并长时间断开连接(参见第11.4节“乐观并发控制”)。
答案是肯定的。 FlushMode
设置:
public enum FlushMode
{
Unspecified = -1,
Never = 0,
Commit = 5,
Auto = 10,
Always = 20,
}
因此,如果FlushMode为Commit
- 只有事务提交才会触发session.Flush()
但我们可以随时随地做到:
// This works
existingChild.CreateParent(parameters);
session.Save(existingChild);
session.Flush();
现在父级将拥有ID - 因为会话状态刚刚转换为SQL WRITE操作...
答案 1 :(得分:0)
您正在使用ID生成策略,该策略仅在对象访问数据库时生成ID。如果您希望在此之前拥有父ID,则需要使用assigned
策略将ID生成带到您的手中。
这是一个很长的话题,我不想在此重复。您可以在这里阅读您需要做的事情: Don't Let Hibernate Steal Your Identity
在文章中,ID类型是UUID。您可以轻松地为int
或long
数据类型实现自定义ID生成器。