【问题标题】:NHibernate table per type persist child from existing parent每个类型的 NHibernate 表从现有父级中持久化子级
【发布时间】:2013-10-15 17:46:04
【问题描述】:

我有 2 个类,Member.csCustomer.cs,并使用 here 中描述的 table-per-type 继承映射。

这个question 提出了同样的问题,但没有答案。

Customer.cs

public class Customer
{
}

Member.cs

public class Member : Customer
{
    public Member(Customer customer)
    {
        CreateFromCustomer(customer);
    }

    private void CreateFromCustomer(Customer customer)
    {
        // Here I assume I'll assign the Id so NHibernate wouldn't have to create a new Customer and know what Customer to be referred
        Id = customer.Id;
    }
}

CustomerMap.cs

public class CustomerMap : ClassMap<Customer>
{
    public CustomerMap()
    {
        Id(x => x.Id)
            .GeneratedBy.GuidComb();
    }
}

MemberMap.cs

public class MemberMap : SubclassMap<Member>
{
    public MemberMap()
    {
        KeyColumn("Id");
    }
}

我尝试了几个测试用例:

Test1.cs

[Test]
public void CanAddCustomer()
{
    var customerRepo = /* blablabla */;

    using (var tx = NHibernateSessionManager.GetSession().BeginTransaction())
    {
        var customer = new Customer()

        customerRepo.RegisterCustomer(customer);

        tx.Commit();
    }

    using (var tx = NHibernateSessionManager.GetSession().BeginTransaction())
    {
        /* Get the persisted customer */
        var customer = customerRepo.GetCustomerByWhatever();

        var member = customerRepo.RegisterMember(new Member(customer));

        tx.Commit();
    }
}

我希望有:

1 名客户和 1 名成员,该成员是该客户的孩子

相反,我有:

2 个客户(1 个是正确创建的,1 个包含所有空列)和 1 个 Id 引用所有空列客户的成员。

这是预期的行为吗?

我知道如果我们想从瞬态父对象创建子对象,这是正确的行为。

但是如果我们要创建一个引用现有父对象的子对象呢?

我提供的链接没有涵盖任何持久性示例,谷歌搜索也没有。

【问题讨论】:

    标签: nhibernate inheritance fluent-nhibernate persistence fluent-nhibernate-mapping


    【解决方案1】:

    简答

    不,不可能将已经持久化的对象“升级”到它的子类。 Nhibernate 根本不支持这一点。这就是为什么您会看到 2 个客户和 1 个成员条目。这实际上是预期的行为,因为 Nhibernate 只是使用对象的新 ID 创建一个副本,而不是创建对 Member 的引用...

    所以基本上你可以做任何一个

    1. 将客户数据复制到会员中,删除客户并保存会员
    2. 使用不带子类的不同对象结构,其中 Member 是具有自己的 ID 和对客户的引用的不同表
    3. 使用原生 sql 将行插入到 Member...

    一些例子:

    您的课程可能如下所示

    public class Customer
    {
        public virtual Guid Id { get; set; }
        public virtual string Name { get; set; }
    }
    public class Member : Customer
    {
        public virtual string MemberSpecificProperty { get; set; }
    }
    

    基本上,Member 可以具有其他属性,但也将具有与 Customer of Cause 相同的属性。

    public class CustomerMap : ClassMap<Customer>
    {
        public CustomerMap()
        {
            Id(x => x.Id)
                .GeneratedBy.GuidComb();
    
            Map(x => x.Name);
        }
    }
    

    对于子类,您只需映射其他属性!

    public class MemberMap : SubclassMap<Member>
    {
        public MemberMap()
        {
            Map(x => x.MemberSpecificProperty);
        }
    }
    

    测试一下

    {
        session.Save(new Customer()
        {
            Name ="Customer A"
        });
    
        session.Save(new Member()
        {
            Name = "Customer B",
            MemberSpecificProperty = "something else"
        });
    
        session.Flush();
    }
    

    这将在客户表中创建 2 个条目,并在成员表中创建一行。所以这符合预期,因为我们创建了一个客户和一个成员......

    现在从客户A“升级”到会员:

    using (var session = NHibernateSessionFactory.Current.OpenSession())
    {
        session.Save(new Customer()
        {
            Name ="Customer A"
        });
    
        session.Flush();
    }
    
    using (var session = NHibernateSessionFactory.Current.OpenSession())
    {
        var customer = session.Query<Customer>().FirstOrDefault();
        //var member = customer as Member;
        var member = new Member()
        {
            Name = customer.Name,
            MemberSpecificProperty = "something else"
        };
        session.Delete(customer);
        session.Save(member);
    
        session.Flush();
    }
    

    【讨论】:

    • 我之前确实尝试过这种方法,但我有些担心:关于 Id 引用,由于 Member 是从 Customer 派生的,它们具有相同的 Id 生成策略,Generated.Guid 说。当我从现有 Customer.Id 分配 Member.Id 时会发生什么? Id 会被持久化,还是会生成新的 Id?我担心如果客户需要在这里有一个 IList 怎么办。而且真正删除客户也很麻烦,因为基本上我喜欢 Cascade.All 策略来持久化新实体。但如有必要,我愿意更改为 Cascade.SaveUpdate。
    • 正如我所写,您不能重用已经存储的 ID A 的客户来创建 ID A 的成员,nHibernate 将生成一个新的 ID B 并将客户+成员行存储在 ID B 的数据库中.
    • 那么当有一些其他数据引用现有客户时如何解决?是否也应该重新创建它们?对整个对象图进行重新创建将是很多工作..
    • @SamuelAdam 是的,正如我所写,重新创建,使用不同的对象策略或使用本机 sql。如果这是您的应用程序中的常见场景,我建议使用不同的策略。意味着没有成员从客户继承......只需建立一对一的关系就可以了,因为这意味着每个客户可能都必须有一个成员
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-07
    相关资源
    最近更新 更多