更新NHibernate版本时遇到问题。当前版本为3.3.1.4000并尝试更新为4。 更新单元测试后,使用级联保存失败:
NHibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [NHibernateTests.TestMappings.ProductLine#cdcaf08d-4831-4882-84b8-14de91581d2e]
映射:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHibernateTests" namespace="NHibernateTests.TestMappings">
<class name="Product" lazy="false" table="UserTest">
<id name="Id">
<generator class="guid"></generator>
</id>
<version name="Version" column="Version" unsaved-value="0"/>
<property name="Name" not-null="false"></property>
<property name="IsDeleted"></property>
<bag name="ProductLines" table="ProductLine" inverse="true" cascade="all" lazy="true" where="IsDeleted=0" >
<cache usage="nonstrict-read-write" />
<key column="UserId" />
<one-to-many class="ProductLine" />
</bag>
</class>
</hibernate-mapping>
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHibernateTests" namespace="NHibernateTests.TestMappings">
<class name="ProductLine" where="IsDeleted=0" lazy="false">
<cache usage="nonstrict-read-write" />
<id name="Id">
<generator class="guid"></generator>
</id>
<version name="Version" column="Version" unsaved-value="0"/>
<property name="IsDeleted"></property>
<many-to-one name="Product" class="Product" column="UserId" not-null="true" lazy="proxy"></many-to-one>
</class>
</hibernate-mapping>
类:
public class Product
{
public Guid Id { get; set; }
public int Version { get; set; }
public bool IsDeleted { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
public IList<ProductLine> ProductLines { get; private set; }
public Product()
{
ProductLines = new List<ProductLine>();
}
}
public class ProductLine
{
public Guid Id { get; set; }
public int Version { get; set; }
public bool IsDeleted { get; set; }
public Product Product { get; set; }
}
测试:
[TestMethod]
public void CascadeSaveTest()
{
var product = new Product
{
Id = Guid.NewGuid(),
Name = "aaa",
IsActive = true
};
var productLine = new ProductLine
{
Id = Guid.NewGuid(),
Product = product,
};
product.ProductLines.Add(productLine);
using (var connection = new RepositoryConnection())
{
using (var repositories = new Repository<Product>(connection))
{
repositories.Create(product);
//the below just calls the Session.Transaction.Commit();
connection.Commit(); //NH3.3.1.400 passes, NH4 fails
}
}
}
提前感谢您的想法。
答案 0 :(得分:1)
嗯,我想我现在已经明白了导致NH 4错误的原因。如果我是对的,那就是一个有点人为的案例,导致这种行为难以成为一个错误。
在您的示例中,产品系列通过bag
进行映射。 bag
可以包含重复项,这需要Product
和ProductLine
之间的中间表。 (类似于ProductProductLine
表格,其中包含UserId
(ProductId)列和ProductLineId
列。)
您已将此中间表设置为ProductLine
表。我怀疑db的提交会导致NHibernate插入产品,然后是产品系列,然后通过再次插入ProductLine
表来尝试插入关系。 (您可以通过在数据库上分析SQL查询来检查它。)
事情有点混乱,因为doc州(强调是我的):
table(可选 - 默认为属性名称)的名称 集合表(不用于一对多关联)
但是,如何尊重允许在集合中重复的bag
语义?来自同一个文档:
行李是无序的,无索引的集合,可能包含相同的集合 元素多次。
无论如何,在您的示例中,您确实应该将one-to-many
映射为set
,如doc所示。
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHibernateTests" namespace="NHibernateTests.TestMappings">
<class name="Product" lazy="false" table="UserTest">
<id name="Id">
<generator class="guid"></generator>
</id>
<version name="Version" column="Version" unsaved-value="0"/>
<property name="Name" not-null="false"></property>
<property name="IsDeleted"></property>
<set name="ProductLines" inverse="true" cascade="all" lazy="true" where="IsDeleted=0" >
<cache usage="nonstrict-read-write" />
<key column="UserId" />
<one-to-many class="ProductLine" />
</set>
</class>
</hibernate-mapping>
并更改您的收藏类型以使用.Net fx 4 System.Collections.Generic.ISet<T>
。
public ISet<ProductLine> ProductLines { get; private set; }
public Product()
{
ProductLines = new HashSet<ProductLine>();
}
如果这会导致你的麻烦消失,那就意味着NH {4}中的bag
处理会发生一些变化。但我们是否应该将此变化视为一个错误?不确定,因为在这种情况下使用bag
看起来不合适。
答案 1 :(得分:0)
进一步深入研究表明,NHibernate4在确定它是一个新实体还是在与Cascade
有关时已经存在的问题上存在问题。
在有问题的情况下,它为Update
而不是ProductLine
调用SQL Create
。
下面的更改可以正常工作,但我对NHibernate版本之间的这种变化感到非常困惑。
更改为ProductLine映射
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHibernateTests" namespace="NHibernateTests.TestMappings">
<class name="ProductLine" where="IsDeleted=0" lazy="false">
<cache usage="nonstrict-read-write" />
<!-- here comes the updated line -->
<id name="Id" type="guid" unsaved-value="00000000-0000-0000-0000-000000000000">
<generator class="guid"></generator>
</id>
<version name="Version" column="Version" unsaved-value="0"/>
<property name="IsDeleted"></property>
<many-to-one name="Product" class="Product" column="UserId" not-null="true" lazy="proxy"></many-to-one>
</class>
</hibernate-mapping>
更改为测试方法
[TestMethod]
public void CascadeSaveTest()
{
var product = new Product
{
Id = Guid.NewGuid(),
Name = "aaa",
IsActive = true
};
var productLine = new ProductLine
{
Id = Guid.Empty, //The updated line
Product = product,
};
product.ProductLines.Add(productLine);
using (var connection = new RepositoryConnection())
{
using (var repositories = new Repository<Product>(connection))
{
repositories.Create(product);
connection.Commit();
}
}
}