我有以下要用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
)
播放器的ScreenName可以是电子邮件地址(“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属性更难,因为我必须从两个遗留的表中获取我无法更改的信息,并且必须以只读方式访问(不允许插入,更新或删除):< / p>
-- 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 for multiple tables,但我发现的所有例子只涉及2个表,而不是3个:
答案 0 :(得分: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添加条件来过滤掉非电子邮件播放器,从而进一步改进选择查询。此外,它还可以让你摆脱遗留类和映射,除非它在应用程序的其他地方使用。
答案 1 :(得分:0)
要补充Denis's answer,我只想添加有关属性公式如何运作的其他文档Hibernate docs:
formula(可选):一个SQL表达式,用于定义a的值 计算属性。计算属性没有列映射 他们自己的。
强大的功能是派生属性。这些属性是由 只读定义。属性值在加载时计算。您 将计算声明为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 )"/>
您可以通过不在a上声明别名来引用实体表 特别栏目。这将是给定示例中的customerId。您 如果你不想要,也可以使用嵌套的映射元素 使用该属性。