使用Fluent NHibernate映射由两列之一连接的集合?

时间:2012-03-19 20:28:01

标签: nhibernate fluent-nhibernate

我正在对会计进行建模,其中我的帐户包含借记一个帐户并记入另一个帐户的交易。

以下是情况的详细信息(简化)。我的表(在SQL Server 2008中)是:

CREATE TABLE Account
(
    AccountID int IDENTITY(1,1) NOT NULL,
    AccountNumber nvarchar(10) NOT NULL
)

CREATE TABLE [Transaction]
(
    TransactionID [bigint] IDENTITY(1,1) NOT NULL,
    DebitAccountID [int] NOT NULL,
    CreditAccountID [int] NOT NULL,
    Amount money NOT NULL
)

我的课程是:

public class Account
{
    public int Id { get; set; }
    public string AccountNumber { get; set; }
    public IList<Transaction> Transactions { get; set; }
}

public class Transaction
{
    public int Id { get; set; }
    public Account DebitAccount { get; set; }
    public Account CreditAccount { get; set; }
}

所以问题是“如何使用流畅的NHibernate在Account类中映射Transactions集合?

我想要的(出于性能原因)是访问事务集合来执行查询:

SELECT ...
FROM [Transaction]
WHERE DebitAccountID=@accountID OR CreditAccountID=@accountID

重要的部分是where子句中的 OR

所以我需要的代码是:

public class AccountMap : SubclassMap<Account>
{
    public AccountMap()
    {
        Id(x => x.Id).Column("AccountID");
        Map(x => x.AccountNumber);
        HasMany(x => x.Transactions)
            // What goes here to explain this mapping to NHibernate?
            .Inverse().Cascade.AllDeleteOrphan()
            .Access.CamelCaseField();
    }
}

注意:我知道我可以将交易映射为两个单独的集合,一个用于“借记”,另一个用于“贷记”。由于性能问题,这是不可接受的答案。特别是,实际上存在与帐户相关的第二种类型(导致更多查询)和映射,因为两个集合阻止了使用Fetch()的急切加载。第二种类型是PaymentScheduleLine,其中包含帐户生命周期内所有正确付款交易的计划。它以与交易相同的方式与帐户相关联,即PaymentScheduleLine具有DebitAccountCreditAccountAccount具有PaymentSchedule集合。通常,复杂的计算涉及交易与付款计划之间的关系。

1 个答案:

答案 0 :(得分:0)

一旦我听到更多关于“与资产相关的第二种类型”,我现在可能会修改我的答案,但现在......

决定什么是重要的,并愿意做出牺牲。

听起来性能是你最关心的问题,对吧?您可能必须放松您的要求,即不更改域模型或查询以某种方式查看。

使用.Future()批量查询以避免笛卡尔积。

你是正确的,拥有名为DebitsCredits 的集合可能会导致性能问题。也许这是您正在考虑的问题:

// BAD QUERY - DO NOT USE - cartesian product of rows - Debits X Credits.
var account = session.QueryOver<Account>()
    .Fetch(x => x.Debits).Eager
    .Fetch(x => x.Credits).Eager
    .Where(x => x.Id == accountId)
    .SingleOrDefault();

如果该帐户有1,000笔交易,其中500笔是借记,500笔,则此查询将导致250,000行(500 * 500),这显然是不可接受的!

但是,您不必以这种方式编写查询。这个更好:

var futureAccount = session.QueryOver<Account>()
    .Fetch(x => x.Debits).Eager
    .Where(x => x.Id == accountId)
    .FutureValue();

session.QueryOver<Account>()
    .Fetch(x => x.Credits).Eager
    .Where(x => x.Id == accountId)
    .Future();

var account = futureAccount.Value;

尽管这实际上是两个查询,但它将在数据库的一次往返中执行,只返回1,000行(500 + 500)。 NHibernate将为两个查询重用相同的Account实例,并只填充急切获取的数据。结果将是Account,其中包含完整填充的DebitsCredits

仅获取所需的数据。

现在,1000行仍然很多。您是否确定需要加载给定帐户的所有交易?你用它们做什么用的?对于您提到的方案,计算在给定时间范围内从帐户A移动到帐户B的金额,如果您编写了一个查询来准确计算,或者至少只加载了您的交易,您将获得更好的性能实际上有兴趣使用更具体的查询。像这样的查询...

var transactions = session.QueryOver<Transaction>()
    .Where(x => x.DebitAccount.Id == fromId
        && x.CreditAccount.Id == toId
        && x.Date >= startDate
        && x.Date < endDate.AddDays(1))
    .List();

...可以轻松地将您正在使用的交易数量从1,000减少到20或更少。