根据NHibernate文档,SQL语句按以下顺序发布:
在我们的案例中,似乎由于某种原因,集合插入发生在实体更新之前。
我们有两个实体,客户端和配置文件,其中客户端拥有并独自负责其配置文件<的子集合/ em>的。个人资料存储有关客户对各种产品的偏好的信息,例如评论和分数。 (简化)类如下所示:
namespace MyProject.Models
{
public class Client
{
public Guid Id { get; set; }
public string Name { get; set; }
public IDictionary<Guid, Profile> Profiles { get; set; }
}
public class Profile
{
public Guid ClientId { get; set; }
public Guid ProfileId { get; set; }
public int Score { get; set; }
public string Comment { get; set; }
}
}
这被映射为:
namespace MyProject.Mappings
{
public class ClientMap : ClassMapping<Client>
{
public ClientMap()
{
Id(c => c.Id, mapping => mapping.Generator(Generators.Assigned));
Property(c => c.Name);
Version(c => c.Version, mapping => mapping.Generated(VersionGeneration.Never));
Map(c =>
c.Profiles,
collectionMapping => collectionMapping.Cascade(Cascade.All | Cascade.DeleteOrphans),
keyMapping => keyMapping.Element(e => e.Column("ProductId")),
valueMapping => valueMapping.OneToMany()
);
}
}
public class ProfileMap : ClassMapping<Profile>
{
public ProfileMap()
{
ComposedId(s =>
{
s.Property(s1 => s1.ClientId);
s.Property(s1 => s1.ProductId);
});
Property(p => p.Score);
Property(p => p.Comment);
}
}
}
产品和配置文件之间没有直接关系。除了上述内容之外,我们还插入了实现FindDirty
的NHibernate会话拦截器架构,并确保在其任何子节点发生变化时发现父版本是脏的。我们还通过在每个事务上设置IsolationLevel.ReadCommitted
来使用乐观并发。
在 ExpressProfiler 中,我们看到以下事件序列(为便于阅读而简化)
SELECT Id, Version
FROM Clients
WHERE Id='3F08F098-09C8-CE42-9594-39C9EAA21B64'
SELECT ClientId, ProductId, Score, Comment
FROM ClientProfiles
WHERE ClientId='3F08F098-09C8-CE42-9594-39C9EAA21B64'
INSERT INTO ClientProfiles (Comment, Score, ClientId, ProductId)
VALUES ('some comment', 123, '3F08F098-09C8-CE42-9594-39C9EAA21B64', '487BC856-6EAF-42E2-ADD6-B3DE056EA714')
UPDATE Clients SET Version = 46 WHERE Id = '3F08F098-09C8-CE42-9594-39C9EAA21B64' AND Version = 45
我们感到困惑的是INSERT
在UPDATE
之前发生的原因。在高度并发的生产系统中,这会导致重复的主键错误而不是StaleObjectStateException
。我们更喜欢后者,因为它更容易确保我们重新安排失败的操作只是因为其他人同时访问了同一条记录。
我们做错了什么?我们是否应该将 Profile 映射为组件而不是类?或者是否有一些我们错过的NHibernate指令?