我正在使用遗留数据库,我正在尝试使用Fluent NHibernate映射某种本地化表。
表格如下:
DataName
[PK] TableName
[PK] FieldName
[PK] Id
[PK] LangId
Description
此表通过此实体进行映射:
public class DataName : EntityBase { public virtual string TableName { get; set; } public virtual string FieldName { get; set; } public virtual int Id { get; set; } public virtual Language Language { get; set; } public virtual string Description { get; set; } ... } public class DataNameMap : ClassMap { public DataNameMap() { Table("zDataName"); CompositeId().KeyProperty(x => x.TableName) .KeyProperty(x => x.FieldName) .KeyProperty(x => x.Id) .KeyProperty(x => x.Language, "LangId").CustomType(); Map(x => x.Description).Column("Descr").Length(2000); } }
为了本地化,我有多个实体映射到“DataName”实体,映射如下所示:
public class Carrier { public virtual int CarrId { get; set; } public virtual string CarrCode { get; set; } public virtual List DataNameList { get; set; } ... } public class CarrierMap : ClassMap { public CarrierMap() { Table("Carriers"); Id(x => x.CarrId).GeneratedBy.Identity(); Map(x => x.CarrCode).Not.Nullable().Length(8); HasMany(x => x.DataNameList) .KeyColumn("Id") .Where("TableName = 'carriers' AND FieldName = 'name'") .Inverse() .Cascade.AllDeleteOrphan(); ... } }
“DataName”表包含如下所示的记录:
TableName FieldName Id LangId Description --------- --------- -- ------ ----------- Carriers Name 1000 1 English description Carriers Name 1000 2 French description
我可以毫无问题地从数据库中检索此信息,但是当需要将新实体(插入或更新)保存到数据库时,NHibernate不会按正确的顺序执行操作。
INSERT INTO DataName (TableName, FieldName, LangId, Id, Description) VALUES ('Carriers', 'Name', 1, NULL, 'English description') INSERT INTO DataName (TableName, FieldName, LangId, Id, Description) VALUES ('Carriers', 'Name', 2, NULL, 'French description') INSERT INTO Carriers....
就本例而言,假设数据库生成的id为2000.
UPDATE DataName SET Id = 2000 WHERE TableName = 'Carriers' AND FieldName = 'Name' AND LangId = 1 AND Id IS NULL UPDATE DataName SET Id = 2000 WHERE TableName = 'Carriers' AND FieldName = 'Name' AND LangId = 2 AND Id IS NULL
我尝试使用我的HasMany映射的Inverse()选项来获取DataNameList集合,但它似乎对这种特殊情况没有任何影响。
如何在插入“Carrier”实体后告诉NHibernate持久保存“Carrier”实体,然后保留“DataName”集合,而不必发出额外的UPDATE语句?
答案 0 :(得分:1)
在这种情况下(可能与遗留数据库相关) NHibernate 正如预期的那样正常工作。我会尝试解释原因。我们必须观察FLUSH Ordering和一列的双重用法:
NHibernate文档sais:
9.6同花顺(http://nhibernate.info/doc/nh/en/index.html#manipulatingdata-flushing)
...
SQL语句按以下顺序发布
- 所有实体插入,使用ISession.Save()
以相同的顺序保存相应的对象- 所有实体更新
- 所有集合删除
- 所有集合元素删除,更新和插入
- 所有收集插入
- 所有实体删除,按相同顺序使用ISession.Delete()删除相应的对象
上面显示的代码段使用一列: Id
(在表DataName中)有两个目的:
我。作为主键(这将在INSERT语句中处理)
CompositeId().KeyProperty(x => x.TableName)
.KeyProperty(x => x.FieldName)
.KeyProperty(x => x.Id)
...
II。作为参考两个实体的载体
HasMany(x => x.DataNameList)
.KeyColumn("Id")
...
鉴于此,答案是: NHibernate 必须插入第一轮中的所有实体。如果存在一些多对一映射,那么它也可以作为INSERT的一部分来完成。因此插入了DataName
和Carrier
。
但在我们的示例中,我们还需要持久化Collection(DataNameList
)。所以现在 NHibernate 更新所有(刚插入的)集合项目,以提供所有者(Carrier
)
所以这就是为什么我会将这种观察到的行为标记为“预期的行为”。为了避免UPDATE,我们可以将列表映射设置为cascade="none"
并手动管理持久性(例如,调用Session.Save(Carrier),然后将Carrier分配给每个DataName并调用Session.Save(DataName),
...
<强>予。生成INSERT和UPDATE的设置
通过这些设置,我能够重复您所经历的行为 C#类载波映射到DataName
private IList<DataName> _dataNames;
public virtual IList<DataName> DataNames
{
get { return _dataNames ?? (_dataNames = new List<DataName>()); }
set { _dataNames = value; }
}
XML映射(与您的流利相同,但与 false 相反)
<bag name="DataNames" inverse="false" cascade="all-delete-orphan"
where="TableName = 'carriers' AND FieldName = 'name'" >
<key column="Id" />
<one-to-many class="DataName" />
</bag>
导致相同问题的代码,您已在上面描述过:
// create 2 DataNames
DataName dn1 = CreateNew(LangId = 1, "English");
DataName dn2 = CreateNew(LangId = 2, "French");
// insert them into collection
carrier.DataNames.Add(dn1);
carrier.DataNames.Add(dn2);
// persist them all
session.Save(carrier);
这导致INSERTS UPDATES
<强> II。工作设置,只需使用INSERT
如果我做出这些改动:
1)将逆转换为 true inverse="true"
2)根据持有者(运营商)
// carrier ID must be get from the server, because of identity
session.Save(carrier);
// explicity setting of the ID
dn1.Id = carrier.ID;
dn2.Id = carrier.ID;
// inverse set to false
carrier.DataNames.Add(dn1);
carrier.DataNames.Add(dn2);
// Update via the carrier entity, will trigger INSERT
session.Update(carrier);
然后只应用INSERTS语句。这有用吗?
注意:如果不在SQL Server(身份)上生成ID,但由NHibernate(HiLo)协助,则可以减少往返。