Свободное отношение таблицы NHibernate 3 без первичных и внешних ключей - PullRequest
0 голосов
/ 10 февраля 2012

Справочная информация

У меня есть следующий класс, который я хочу сопоставить с 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") или чем-то подобным.Сопоставить первые три свойства Player с помощью Fluent NHibernate достаточно просто:

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);
    }
}

Но свойство Unsubscribeed сложнее, потому что мне нужно получить эту информацию из двух устаревших таблиц, которые я не могу изменить, и которые ядолжен иметь доступ только для чтения (никакие вставки, обновления или удаления не разрешены):

-- 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, ни вНеподписанная таблица.

Это классы унаследованных таблиц:

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; }
}

И вот их беглые отображения:

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");
    }
}

Задача

Проблема, с которой я столкнулся при попытке получить отписанную информацию для игроков по электронной почте.

Единственный способ, которым я могу связать таблицу Unsubscribeed с таблицей Player, - через промежуточную таблицу EmailAddress, соответствующуюEmailAddress.EmailAddress для Player.AddressIdentifier, но у меня возникают проблемы при попытке выяснить, как это сделать с помощью Fluent NHibernate.

Я посмотрел на Соединение для нескольких таблиц, но все примеры, которые я нашел, касаются только двух таблиц, а не трех:

  1. Объединение таблиц с использованиемСвободный NHibernate
  2. Свободный Nhibernate левое соединение

Ответы [ 2 ]

1 голос
/ 11 февраля 2012

Одним из возможных решений было бы использование формулы для свойства 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, чтобы отфильтровать игроков, не являющихся пользователями электронной почты.Кроме того, он позволяет избавиться от устаревших классов и отображений, если только он не используется где-либо еще в приложении.

0 голосов
/ 28 июля 2014

В дополнение к ответу Дениса , я просто хотел добавить дополнительную документацию о том, как работают формулы свойств, из документов Hibernate :

(необязательно): выражение SQL, которое определяет значение для вычисляемого свойства.Вычисляемые свойства не имеют собственного сопоставления столбцов.

Мощная функция - это производные свойства.Эти свойства по определению только для чтения.Значение свойства вычисляется во время загрузки.Вы объявляете вычисление как выражение SQL.Затем это преобразуется в подзапрос предложения SELECT в запросе SQL, который загружает экземпляр:

<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 в данном примере.Вы также можете использовать вложенный элемент отображения, если не хотите использовать атрибут.

...