Связи между объектами в Fluent NHibernate

Fluent NHibernate позволяет задавать связи один к одному, один ко многим и многие ко многим посредством специальных конструкций: HasOne, HasMany, HasManyToMany.

Рассмотрим примеры создания этих трех типов связей на примерах.

Связь один к одному

Для связи один к одному возьмем объекты «Студент» и «Зачетная книжка» - один студент может иметь только одну зачетную книжку. Для начала создадим классы студента и зачетной книжки:

namespace Fluent.Domain

{

//Домен студента

public class Student

{

public virtual long Id { get; set; }

public virtual string FirstName { get; set; }

public virtual string LastName { get; set; }

public virtual char Sex { get; set; }

public virtual int Year { get; set; }

//Ссылка на зачетную книжку

public virtual RecordBook RecordBook { get; set; }

}

}

namespace Fluent.Domain

{

//Домен зачетной книжки

public class RecordBook

{

public virtual long Id { get; set; }

public virtual string Number { get; set; }

//Ссылка на студента

public virtual Student Student { get; set; }

}

}

Теперь необходимо создать классы отображения (map-классы):

namespace Fluent.Mappings

{

//Класс отображения зачетной книжки

public class RecordBookMap : ClassMap<RecordBook>

{

public RecordBookMap()

{

//Указание имени таблицы для зачетной книжки

Table("RecordBooks");

//Отображение идентификатора на колонку таблицы

Id(x => x.Id).GeneratedBy.Native();

//Отображение обычного поля на колонку таблицы

Map(x => x.Number);

//Ссылка на студента

References(x => x.Student).Column("StudentId").Cascade.All();

}

}

}

namespace Fluent.Mappings

{

//Класс отображения студента

public class StudentMap : ClassMap<Student>

{

public StudentMap()

{

//Указание имени таблицы для студента

Table("Students");

Id(x => x.Id).GeneratedBy.Native();

Map(x => x.FirstName);

Map(x => x.LastName);

Map(x => x.Sex);

Map(x => x.Year);

//Связь один к одному

HasOne(x => x.RecordBook).ForeignKey("StudentId").Cascade.All();

}

}

}

Как видно из примера, каждый класс отображения содержит конструкцию для указания имени таблицы в базе данных (Table), конструкцию для отображения ключевого поля на таблицу базы данных (Id), конструкции для отображения информационных полей на таблицу базы данных (Map), конструкции для связывания объектов. В данном примере, для того чтобы связать объекты «Студент» и «Зачетная книжка» связью один к одному в классе студента необходима ссылка на объект «Зачетная книжка»:

public virtual RecordBook RecordBook { get; set; }

В классе зачетной книжки для того, чтобы с объекта зачетной книжки можно было получить доступ к студенту, в классе зачетной книжки необходима ссылка на объект «Студент»:

public virtual Student Student { get; set; }

В классах отображения тоже должны быть соответствующие поля. Со стороны студента:

HasOne(x => x.RecordBook).ForeignKey("StudentId").Cascade.All();

Со стороны зачетной книжки должно быть поле:

References(x => x.Student).Column("StudentId").Cascade.All();

В соответствии с классами отображения в базе данных автоматически создаются таблицы, изображенные на рисунке 4.1 и 4.2.

Связи между объектами в Fluent NHibernate - student2.ru

Рисунок 4.1 – Таблица зачетной книжки

Связи между объектами в Fluent NHibernate - student2.ru

Рисунок 4.2 – Таблица студента

Рассмотрим теперь связь один ко многим.

Связь один ко многим

Для связи один ко многим возьмем объекты «Группа» и «Студент». В одной группе может быть много студентов. Один студент может состоять только в одной группе.

Создадим классы домены для группы и студента:

namespace Fluent.Domain

{

//Домен группы

public class Group

{

private IList<Student> studentList = new List<Student>();

public virtual long Id { get; set; }

public virtual string GroupName { get; set; }

public virtual string CuratorName { get; set; }

public virtual string HeadmanName { get; set; }

public virtual IList<Student> StudentList

{

get { return studentList; }

set { studentList = value; }

}

}

}

namespace Fluent.Domain

{

//Домен студента

public class Student

{

public virtual long Id { get; set; }

public virtual string FirstName { get; set; }

public virtual string LastName { get; set; }

public virtual char Sex { get; set; }

public virtual int Year { get; set; }

public virtual Group Group { get; set; }

}

}

Обратите внимание, что со стороны группы содержится список для хранения студентов.

Создадим классы отображения для группы и студента:

using System;

using System.Collections.Generic;

using FluentNHibernate.Mapping;

using Fluent.Domain;

namespace Fluent.Mappings

{

//Класс отображения группы

public class GroupMap:ClassMap<Group>

{

public GroupMap()

{

Table("Groups");

Id(x => x.Id).GeneratedBy.Native();

Map(x => x.GroupName);

Map(x => x.CuratorName);

Map(x => x.HeadmanName);

//Связь один ко многим

HasMany(x => x.StudentList)

.KeyColumns.Add("GroupId")

Inverse()

Cascade.All();

}

}

}

namespace Fluent.Mappings

{

//Класс отображения группы

public class StudentMap : ClassMap<Student>

{

public StudentMap()

{

Table("Students");

Id(x => x.Id).GeneratedBy.Native();

Map(x => x.FirstName);

Map(x => x.LastName);

Map(x => x.Sex);

Map(x => x.Year);

//Ссылка на руппу

References(x => x.Group).Column("GroupId").Cascade.All();

}

}

}

В отличие от связи один к одному, со стороны один в классе отображения находится конструкция:

HasMany(x => x.StudentList)

.KeyColumns.Add("GroupId")

Inverse()

Cascade.All();

А со стор многие, как и в связи один к одному находится конструкция:

References(x => x.Group).Column("GroupId").Cascade.All();

В соответствии с классами отображения в базе данных автоматически создаются таблицы, изображенные на рисунке 4.1 и 4.2.

Связи между объектами в Fluent NHibernate - student2.ru

Рисунок 4.3 – Таблица группы

Связи между объектами в Fluent NHibernate - student2.ru

Рисунок 4.4 – Таблица студента

Рассмотрим теперь связь многие ко многим

Связь многие ко многим

Для связи многие ко многим выберем объекты «Преподаватель» и «Предмет». Один преподаватель может вести много предметов и один предмет может вести несколько преподавателей.

Создадим классы домены для преподавателя и для предмета:

namespace Fluent.Domain

{

//Класс домена предмета

public class Subject

{

private IList<Teacher> teacherList = new List<Teacher>();

public virtual long Id { get; set; }

public virtual string SubjectName { get; set; }

public virtual int HoursNumber { get; set; }

public virtual IList<Teacher> TeacherList

{

get { return teacherList; }

set { teacherList = value; }

}

}

}

namespace Fluent.Domain

{

//Класс домена преподавателя

public class Teacher

{

private IList<Subject> subjectList = new List<Subject>();

public virtual long Id { get; set; }

public virtual string FirstName { get; set; }

public virtual string LastName { get; set; }

public virtual IList<Subject> SubjectList

{

get { return subjectList; }

set { subjectList = value; }

}

}

}

Обратите внимание, что класс предмета содержит коллекцию для хранения преподавателей, а класс преподавателей содержит коллекцию для хранения предметов.

Создадим классы отображения для предмета и преподавателя:

namespace Fluent.Mappings

{

//Класс отображения предмета

public class SubjectMap:ClassMap<Subject>

{

public SubjectMap()

{

Table("Subjects");

Id(x => x.Id).GeneratedBy.Native();

Map(x => x.SubjectName);

Map(x => x.HoursNumber);

//Связь многие ко многим

HasManyToMany(x => x.TeacherList)

.Table("TeacherSubject")

.ParentKeyColumn("SubjectId")

.ChildKeyColumn("TeacherId");

}

}

}

namespace Fluent.Mappings

{

//Класс отображения преподавателя

public class TeacherMap:ClassMap<Teacher>

{

public TeacherMap()

{

Table("Teachers");

Id(x => x.Id).GeneratedBy.Native();

Map(x => x.FirstName);

Map(x => x.LastName);

//Связь многие ко многим

HasManyToMany(x => x.SubjectList)

.Table("TeacherSubject")

.ParentKeyColumn("TeacherId")

.ChildKeyColumn("SubjectId");

}

}

}

Обратите внимание, что в классах отображениях с обеих сторон указывается конструкция HasManyToMany

Поскольку связь многие ко многим в реляционных базах данных осуществляется через отдельную таблицу, то в базе данных будет автоматически создано три таблицы, которые представлены на рисунках 4.5, 4.6 и 4.7.

Связи между объектами в Fluent NHibernate - student2.ru

Рисунок 4.5 – Таблица предмета

Связи между объектами в Fluent NHibernate - student2.ru

Рисунок 4.6 – Таблица преподавателя

Связи между объектами в Fluent NHibernate - student2.ru

Рисунок 4.7 – Промежуточная таблица преподавателя и предмета

Более детально со связями между объектами, а также с параметрами связей можно ознакомиться в книгах NHibernate in Action и NHibernate Cookbook.

Наши рекомендации