我正在对会计进行建模,其中我的帐户包含借记一个帐户并记入另一个帐户的交易。
以下是情况的详细信息(简化)。我的表(在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
具有DebitAccount
和CreditAccount
,Account
具有PaymentSchedule
集合。通常,复杂的计算涉及交易与付款计划之间的关系。
答案 0 :(得分:0)
一旦我听到更多关于“与资产相关的第二种类型”,我现在可能会修改我的答案,但现在......
听起来性能是你最关心的问题,对吧?您可能必须放松您的要求,即不更改域模型或查询以某种方式查看。
.Future()
批量查询以避免笛卡尔积。你是正确的,拥有名为Debits
和Credits
的集合可能会导致性能问题。也许这是您正在考虑的问题:
// 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
,其中包含完整填充的Debits
和Credits
。
现在,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或更少。