0

背景资料

我有以下要使用 NHibernate 映射的类:

public class Player
{
    public virtual int Id { get; set; }
    public virtual Type Type { get; set; }
    public virtual string ScreenName { get; set; }
    public virtual bool Unsubscribed { get; set; }
}

在数据库方面,我有以下表格:

-- New table
Player (
    int Id
    int TypeId (not null) -- foreign-key to Type table
    string ScreenName (not null) -- can be an EmailAddress, but not necessarily
)
Type (
    int Id
    string Name -- "Email", "Facebook", etc
)

玩家的屏幕名称可以是电子邮件地址 ("foo@bar.com")、Twitter 屏幕名称 ("@FooBar")、Skype 屏幕名称 ("foo.bar") 或其他类似名称。使用 Fluent NHibernate 映射 Player 的前三个属性非常简单:

public class PlayerMap : ClassMap<Player>
{
    public PlayerMap()
    {
        Id(x => x.Id);
        Map(x => x.ScreenName)
            .Not.Nullable();
        References(x => x.Type)
            .Column("TypeId")
    }
}

public class TypeMap : ClassMap<Type>
{
    public TypeMap()
    {
        Id(x => x.Id);
        Map(x => x.Name);
    }
}

但是 Unsubscribed 属性更难,因为我必须从两个无法更改且必须以只读方式访问的旧表中获取该信息(不允许插入、更新或删除):

-- Legacy tables, can't change
EmailAddress (
    int Id
    string EmailAddress (not null) -- "foo@bar.com"
)
Unsubscribed (
    int Id
    int EmailAddressId (not null) -- foreign key to EmailAddress table
)

只有电子邮件玩家可以取消订阅,所以其他类型的玩家在 EmailAddress 和 Unsubscribed 表中都不会有一行。

这些是遗留表的类:

public class EmailAddress
{
    public virtual int Id { get; set; }
    public virtual string Value { get; set; }
    public virtual IList<Unsubscription> Unsubscriptions{ get; set; }
}

public class Unsubscription
{
    public virtual int Id { get; set; }
    public virtual EmailAddress EmailAddress { get; set; }
}

以下是他们的 Fluent 映射:

public class EmailAddressMap : ClassMap<EmailAddress>
{
    public EmailAddressMap()
    {
        ReadOnly();
        Id(x => x.Id);
        Map(x => x.Value)
            .Column("EmailAddress")
            .Not.Nullable();
        HasMany(x => x.Unsubscriptions)
            .KeyColumn("EmailAddressId");
    }
}

public class EmailOptOutMap : ClassMap<EmailOptOut>
{
    public EmailOptOutMap()
    {
        ReadOnly();
        Id(x => x.Id);
        References(x => x.EmailAddress)
            .Column("EmailAddressId");
    }
}

问题

我遇到的问题是尝试获取电子邮件播放器的未订阅信息。

我可以将 Unsubscribed 表与 Player 表关联的唯一方法是通过中间 EmailAddress 表,将 EmailAddress.EmailAddress 与 Player.AddressIdentifier 匹配,但我在尝试弄清楚如何使用 Fluent NHibernate 执行此操作时遇到了麻烦。

我查看了多个表的 Join ,但我发现的所有示例都只处理 2 个表,而不是三个:

  1. 使用 Fluent NHibernate 加入表
  2. Fluent Nhibernate 左连接
4

2 回答 2

1

一种可能的解决方案是使用只读Unsubscribed属性的公式来根据存储在旧表中的数据动态填充其值。您可以将其映射如下:

Map(x => x.Unsubscribed).Formula("(CASE WHEN EXISTS (SELECT EA.Id FROM EmailAddress EA INNER JOIN Unsubscribed ON EA.Id = Unsubscribed.EmailAddressId WHERE EA.EmailAddress = ScreenName) THEN 1 ELSE 0 END)").ReadOnly();

当然,您可以通过为 TypeId 添加条件来过滤掉非电子邮件玩家,从而进一步改进选择查询。此外,它允许您摆脱遗留类和映射,除非它在应用程序的其他地方使用。

于 2012-02-10T22:44:47.230 回答
0

为了补充丹尼斯的回答,我只想从Hibernate 文档中添加关于属性公式如何工作的附加文档

公式(可选):定义 计算属性值的 SQL 表达式。计算属性没有自己的列映射。

一个强大的功能是派生属性。根据定义,这些属性是只读的。属性值在加载时计算。您将计算声明为 SQL 表达式。然后,这将转换为加载实例的 SQL 查询中的 SELECT 子句子查询:

<property name="totalPrice" formula="( SELECT SUM (li.quantity*p.price) FROM LineItem li, Product p WHERE li.productId = p.productId AND li.customerId = customerId AND li.orderNumber = orderNumber )"/>

您可以通过不在特定列上声明别名来引用实体表。这将是给定示例中的 customerId。如果您不想使用该属性,也可以使用嵌套映射元素。

于 2014-07-28T07:11:52.800 回答