【问题标题】:NHibernate: How is identity Id updated when saving a transient instance?NHibernate:保存瞬态实例时如何更新身份 ID?
【发布时间】:2011-06-21 05:26:53
【问题描述】:

如果我使用 session-per-transaction 并调用:

session.SaveOrUpdate(entity) 更正:
session.SaveOrUpdateCopy(entity)

..and entity 是一个临时实例,identity-Id=0。上面的行是否应该自动更新实体的Id,并使实例持久化?还是应该在 transaction.Commit 上这样做?还是我必须以某种方式明确编码?

显然,数据库行的 ID(新的,因为是瞬态的)是自动生成的并保存为某个数字,但我在这里谈论的是实际参数实例。哪个是业务逻辑实例。


EDIT - 跟进相关问题。

映射:

public class StoreMap : ClassMap<Store>
{
    public StoreMap()
    {
        Id(x => x.Id).GeneratedBy.Identity();
        Map(x =>  x.Name);
        HasMany(x => x.Staff)    // 1:m
            .Cascade.All();       
        HasManyToMany(x => x.Products)  // m:m
            .Cascade.All()
            .Table("StoreProduct");    
    }
}

public class EmployeeMap : ClassMap<Employee> 
{
    public EmployeeMap()
    {
        Id(x => x.Id).GeneratedBy.Identity();   
        Map(x => x.FirstName);
        Map(x => x.LastName);
        References(x => x.Store);    // m:1
    }
}

public class ProductMap : ClassMap<Product>
{
    public ProductMap() 
    {
        Id(x => x.Id).GeneratedBy.Identity();
        Map(x => x.Name).Length(20);
        Map(x => x.Price).CustomSqlType("decimal").Precision(9).Scale(2);
        HasManyToMany(x => x.StoresStockedIn)
        .Cascade.All()
        .Inverse()
        .Table("StoreProduct");
     } 
}

EDIT2

类定义:

   public class Store
{
    public int Id { get; private set; }
    public string Name { get; set; }
    public IList<Product> Products { get; set; }
    public IList<Employee> Staff { get; set; }

    public Store()
    {
        Products = new List<Product>();
        Staff = new List<Employee>();
    }


    // AddProduct & AddEmployee is required. "NH needs you to set both sides before
    // it will save correctly" 

    public void AddProduct(Product product)
    {
        product.StoresStockedIn.Add(this);
        Products.Add(product);
    }

    public void AddEmployee(Employee employee)
    {
        employee.Store = this;
        Staff.Add(employee);
    }
}

public class Employee
{
    public int Id { get;  private set; }
    public string FirstName { get;  set; }
    public string LastName { get;  set; }
    public Store Store { get; set; }
}

public class Product
{
    public int Id { get; private set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
    public IList<Store> StoresStockedIn { get; private set; }
}

【问题讨论】:

    标签: c# .net database nhibernate fluent-nhibernate


    【解决方案1】:

    我注意到我通过调用保存了:

    session.SaveOrUpdateCopy(entity); 
    

    ..它不会更新 Id。但是通过更改为:

    session.SaveOrUpdate(entity);
    

    ..临时实体的 ID 更新。

    我可能误解了documentation (?).. Section 9.4.2 的说法:

    SaveOrUpdateCopy(Object o)...如果给定的实例未保存或在数据库中不存在,NHibernate 将保存它并将其作为新的持久化实例返回。 em>

    只是我,还是听起来不像是临时对象(未保存),会“作为持久性返回”?这不意味着更新的ID吗?希望澄清如何正确解释这句话(?)

    【讨论】:

    • 您不应该使用SaveOrUpdateCopy,除非您了解它的作用,并且它适用于您的情况;默认情况下始终使用SaveOrUpdateSaveOrUpdate 将保存或更新您的实体实例,如果它是暂时的,则更新 id 属性。 SaveOrUpdateCopy 将返回一个 id 已更改的 新对象;您调用该方法的任何对象都将保持不变。
    【解决方案2】:

    就您的问题而言,每当您刷新会话时,就是您的实体被持久化到数据库中。保存您的(新)实体时,NHibernate 使用您提供的生成器为您生成 ID。

    请记住,不推荐使用身份生成器(请参阅this post by Ayende)。 当您使用身份生成器时,即使您没有刷新到数据库,您的新实体也会在您保存时持久保存到数据库中。发生这种情况的原因是 NHibernate 需要为您提供实体的 ID,如果不往返数据库,它就无法做到这一点。

    如果您想要“正常”值,更好的解决方案是使用 Guid 生成器或 HiLo 之类的东西。通过这种方式,您可以保存您的实体,而无需实际执行数据库往返,这使您可以在性能方面做更多的事情(想到批处理)。

    【讨论】:

    • 有趣.. 所以如果我通过循环调用每个项目的 foreach 来保存集合,每次保存都会访问数据库?完整的行会保存在 session.Save 上吗?
    • 我必须用分析器仔细检查,但很可能是这样。
    【解决方案3】:

    Nhibernate 将在 SaveOrUpdate 调用之后设置实体的 ID 属性。

    【讨论】:

    • 我想这是有道理的,因为会话范围可能很长。但我的 ID 仍然是 0..
    • FluentNHibernate 由 OP 的标签判断。
    • 当映射中的 id 属性的 Name 属性未设置时,通常不会设置 Id。但我认为 Fluent 默认会这样做
    • 是的,我用流利的方式映射:Id(x => x.Id).GeneratedBy.Identity();现在也试过 Id(x => x.Id).Column("Id").GeneratedBy.Identity();但没有帮助。
    【解决方案4】:

    我不确定我是否理解您的问题。当会话被刷新时(例如通过提交事务),实际保存到数据库中。调用 SaveOrUpdate() 本身并不会保存实体,它只是通知会话当会话被刷新时实体将被保存。

    假设实体的ID映射到数据库中的一个身份字段,并且你的映射告诉NHibernate身份是由数据库设置的,那么数据库设置的ID将在保存时设置为实体的ID .

    【讨论】:

    • 是的,你的意思是;在代码行 transaction.Commit() 之后,实体的 Id 属性应反映该设置到数据库行吗?我问的原因是我跟踪代码(在我的 repository.SaveOrUpdate 方法中),实体的 id 一直是 0,即使在 .Commit 之后也是如此。所以我想确切地确认它应该在什么时候设置。
    • 如果你的id在提交后为0,说明你没有正确映射生成器。
    • 谢谢!得到坚定的确认很好。那么肯定和我的另一个问题stackoverflow.com/questions/4890123/…有关。我认为这个身份问题可能是导致那个问题的原因。但后来我认为这是一个结果,而不是一个原因。顺便提一句; “生成器”是指标准的实体类映射吗?还是 ISessionFactory 配置?
    • 更改 ID(x => x.Id);到 Id(x => x.Id).GeneratedBy.Identity();
    • 我这样做了(以为它是默认自动生成的,但不确定)。但它仍然没有更新实体 ID。
    猜你喜欢
    • 2013-11-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-24
    • 2012-02-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多