Что такое port mapping? Технология проведения миграции данных в крупных проектах


В предыдущей части были рассмотрены виды связей (один-к-одному, один-ко-многим, многие-ко-многим), а также один класс Book и его маппинг-класс BookMap. Во второй части обновим класс Book, создадим остальные классы и связи между ними, как это было изображено в предыдущей главе в Диаграмме баз данных, расположившейся над подзаголовком 1.3.1 Связи.

Код классов и маппингов (С комментариями)

Класс Книга

Public class Book { //Уникальный идентификатор public virtual int Id { get; set; } //Название public virtual string Name { get; set; } //Описание public virtual string Description { get; set; } //Оценка Мира фантастики public virtual int MfRaiting { get; set; } //Номера страниц public virtual int PageNumber { get; set; } //Ссылка на картинку public virtual string Image { get; set; } //Дата поступления книги (фильтр по новинкам!) public virtual DateTime IncomeDate { get; set; } //Жанр (Многие-ко-Многим) //Почему ISet а не IList? Только одна коллекция (IList) может выбираться с помощью JOIN выборки, если нужно более одной коллекции для выборки JOIN, то лучше их преобразовать в коллекцию ISet public virtual ISet Genres { get; set; } //Серия (Многие-к-одному) public virtual Series Series { get; set; } //Мнение и другое (Один-к-одному) private Mind _mind; public virtual Mind Mind { get { return _mind ?? (_mind = new Mind()); } set { _mind = value; } } //Автор (Многие-ко-многим) public virtual ISet Authors { get; set; } //Заранее инициализируем, чтобы исключение null не возникало. public Book() { //Неупорядочное множество (в одной таблице не может присутствовать две точь-в-точь одинаковые строки, в противном случае выбирает одну, а другую игнорирует) Genres = new HashSet(); Authors = new HashSet(); } } //Маппинг класса Book public class BookMap: ClassMap { public BookMap() { Id(x => x.Id); Map(x => x.Name); Map(x => x.Description); Map(x => x.MfRaiting); Map(x => x.PageNumber); Map(x => x.Image); Map(x => x.IncomeDate); //Отношение многие-ко-многим HasManyToMany(x => x.Genres) //Правила каскадирования All - Когда объект сохраняется, обновляется или удаляется, проверяются и //создаются/обновляются/добавляются все зависимые объекты.Cascade.SaveUpdate() //Название промежуточной таблицы ДОЛЖНО быть как и у класса Genre! .Table("Book_Genre"); HasManyToMany(x => x.Authors) .Cascade.SaveUpdate() .Table("Book_Author"); //Отношение многие к одному References(x => x.Series); //Отношение один-к-одному. Главный класс. HasOne(x => x.Mind).Cascade.All().Constrained(); } }

Public class Author { public virtual int Id { get; set; } //Имя-Фамилия public virtual string Name { get; set; } //Биография public virtual string Biography { get; set; } //Книжки public virtual ISet Books { get; set; } //Инициализация Авторов public Author() { Books=new HashSet(); } } //Маппинг Автора public class AuthorMap: ClassMap { public AuthorMap() { Id(x => x.Id); Map(x => x.Name); Map(x => x.Biography); //Отношение многие-ко-многим HasManyToMany(x => x.Books) //Правила каскадирования All - Когда объект сохраняется, обновляется или удаляется, проверяются и создаются/обновляются/добавляются все зависимые объекты.Cascade.All() //Владельцем коллекции явл. другой конец отношения (Book) и он будет сохранен первым. .Inverse() //Название промежуточной таблицы ДОЛЖНО быть как и у класса Book! .Table("Book_Author"); } }

Класс Жанр

Public class Genre { public virtual int Id { get; set; } //Название жанра public virtual string Name { get; set; } //Английское название жанра public virtual string EngName { get; set; } //Книжки public virtual ISet Books { get; set; } //Инициализация книг public Genre() { Books=new HashSet(); } } //Маппинг жанра public class GenreMap: ClassMap { public GenreMap() { Id(x => x.Id); Map(x => x.Name); Map(x => x.EngName); //Отношение многие-ко-многим HasManyToMany(x => x.Books) //Правила каскадирования All - Когда объект сохраняется, обновляется или удаляется, проверяются и создаются/обновляются/добавляются все зависимые объекты.Cascade.All() //Владельцем коллекции явл. другой конец отношения (Book) и он будет сохранен первым. .Inverse() //Название промежуточной таблицы ДОЛЖНО быть как и у класса Book! .Table("Book_Genre"); } }

Класс Мнение:

Public class Mind { public virtual int Id { get; set; } //Мое мнение public virtual string MyMind { get; set; } //Мнение фантлаба public virtual string MindFantLab { get; set; } //Книга public virtual Book Book { get; set; } } //Маппинг Мind public class MindMap:ClassMap { public MindMap() { Id(x => x.Id); Map(x => x.MyMind); Map(x => x.MindFantLab); //Отношение один к одному HasOne(x => x.Book); } }

Класс Цикл(Серия):

Public class Series { public virtual int Id { get; set; } public virtual string Name { get; set; } //Я создал IList, а не ISet, потому что кроме Book, Series больше ни с чем не связана, хотя можно сделать и ISet public virtual IList Books { get; set; } //Инициализация книг. public Series() { Books = new List(); } } public class SeriesMap: ClassMap { public SeriesMap() { Id(x => x.Id); Map(x => x.Name); //Отношение один-ко-многим HasMany(x => x.Books) ////Владельцем коллекции явл. другой конец отношения (Book) и он будет сохранен первым. .Inverse() } }

Небольшое объяснение
public virtual ISet Genres { get; set; }
public virtual ISet Authors { get; set; }

Почему ISet, а не, к примеру, привычный многим IList? Если использовать вместо ISet - IList, и попробовать запустить проект, то разницы особой мы не заметим (Таблицы и классы создадутся). Но когда мы к классу Book LeftJoin-им одновременно таблицу Genre и Authors, да и еще пытаемся вывести неповторяющиеся записи из таблицы Book (Distinct Book.Id) в представление (View), Nhibernate выдаст исключение и ошибку.
Cannot simultaneously fetch multiple bags.
В таких случаях используем ISet, тем более множества для этого и предназначены (игнорируют дублирующие записи).

Отношение многие-ко-многим.

В NHibernate есть понятие, «главной» таблицы. Хотя отношения «многие-ко-многим» между таблицами “Book” и “Автор” равнозначны (У автора может быть много книг, у книги может быть множество авторов), Nhibernate требует, чтобы программист указывал таблицу, которая сохраняется второй (имеет метод.inverse()), то есть вначале будет создана/обновлена/удалена запись в таблице Book, а только потом в таблице Author.
Cascade.All означает выполнение каскадных операций при save-update и delete. То есть когда объект сохраняется, обновляется или удаляется, проверяются и создаются/обновляются/добавляются все зависимые объекты (Ps. Можно прописать вместо Cascade.All -> .Cascade.SaveUpdate().Cascade.Delete())
Метод.Table(«Book_Author»); создает «промежуточную» таблицу “Book_Author” в БД.

Отношение многие-к-одному, один-ко-многим.

Метод.Constrained() говорит NHibernate, что для записи из таблицы Book должна соответствовать запись из таблицы Mind (id таблицы Mind должен быть равен id таблицы Book)

Если сейчас запустить проект и посмотреть БД Bibilioteca, то появятся новые таблицы с уже сформированными связями.

Далее заполним созданные таблицы данными…
Для этого создадим тестовое приложение, которое будет сохранять данные в БД, обновлять и удалять их, изменив HomeController следующим образом (Ненужные участки кода комментируем):
public ActionResult Index() { using (ISession session = NHibernateHelper.OpenSession()) { using (ITransaction transaction = session.BeginTransaction()) { //Создать, добавить var createBook = new Book(); createBook.Name = "Metro2033"; createBook.Description = "Постапокалипсическая мистика"; createBook.Authors.Add(new Author { Name = "Глуховский" }); createBook.Genres.Add(new Genre { Name = "Постапокалипсическая мистика" }); createBook.Series = new Series { Name = "Метро" }; createBook.Mind = new Mind { MyMind = "Постапокалипсическая мистика" }; session.SaveOrUpdate(createBook); //Обновить (По идентификатору) //var series = session.Get(1); //var updateBook = session.Get(1); //updateBook.Name = "Metro2034"; //updateBook.Description = "Антиутопия"; //updateBook.Authors.ElementAt(0).Name = "Глуховский"; //updateBook.Genres.ElementAt(0).Name = "Антиутопия"; //updateBook.Series = series; //updateBook.Mind.MyMind = "11111"; //session.SaveOrUpdate(updateBook); //Удаление (По идентификатору) //var deleteBook = session.Get(1); //session.Delete(deleteBook); transaction.Commit(); } Genre genreAl = null; Author authorAl = null; Series seriesAl = null; Mind mindAl = null; var books = session.QueryOver() //Left Join с таблицей Genres .JoinAlias(p => p.Genres, () => .JoinAlias(p => p.Authors, () => authorAl, JoinType.LeftOuterJoin) .JoinAlias(p => p.Series, () => seriesAl, JoinType.LeftOuterJoin) .JoinAlias(p => p.Mind, () => mindAl, JoinType.LeftOuterJoin) //Убирает повторяющиеся id номера таблицы Book. .TransformUsing(Transformers.DistinctRootEntity).List(); return View(books); } }

Небольшое объяснение

  1. var books = session.QueryOver() Select * From Book ;
  2. .JoinAlias(p => p.Genres, () => genreAl, JoinType.LeftOuterJoin) - подобно выполнению скрипта SQL:
    SELECT *FROM Book
    inner JOIN Book_Genre ON book.id = Book_Genre.Book_id
    LEFT JOIN Genre ON Book_Genre.Genre_id = Genre.id
  3. .TransformUsing(Transformers.DistinctRootEntity) - Подобно выполнению скрипта SQL: SELECT distinct Book.Id... , (убирает дублирующие записи с одинаковыми id)

Виды объединений
.JoinAlias(p => p.Genres, () => genreAl, JoinType.LeftOuterJoin)

  1. LeftOuterJoin - выбирает все записи из левой таблицы (Book ), а затем присоединяет к ним записи правой таблицы (Genre ). Если не найдена соответствующая запись в правой таблицы, отображает её как Null
  2. RightOuterJoin действует в противоположность LEFT JOIN - выбирает все записи из правой таблицы (Genre ), а затем присоединяет к ним записи левой таблицы (Book )
  3. InnerJoin - выбирает только те записи из левой таблиц (Book ) у которой есть соответствующая запись из правой таблицы (Genre ), а затем присоединяет к ним записи из правой таблицы

Изменим представление следующим образом:

Представление index

@model IEnumerable @{ Layout = null; } Index

@Html.ActionLink("Create New", "Create")

@foreach (var item in Model) { @{string strSeries = item.Series != null ? item.Series.Name: null;} }
@Html.DisplayNameFor(model => model.Name) @Html.DisplayNameFor(model => model.Mind) @Html.DisplayNameFor(model => model.Series) @Html.DisplayNameFor(model => model.Authors) @Html.DisplayNameFor(model => model.Genres) Операции
@Html.DisplayFor(modelItem => item.Name) @Html.DisplayFor(modelItem => item.Mind.MyMind)@Html.DisplayFor(modelItem => strSeries) @foreach (var author in item.Authors) { string strAuthor = author != null ? author.Name: null; @Html.DisplayFor(modelItem => strAuthor)
}
@foreach (var genre in item.Genres) { string strGenre = genre!= null ? genre.Name: null; @Html.DisplayFor(modelItem => strGenre)
}
@Html.ActionLink("Edit", "Edit", new { id = item.Id }) | @Html.ActionLink("Details", "Details", new { id = item.Id }) | @Html.ActionLink("Delete", "Delete", new { id = item.Id })


Проверив поочередно все операции, мы заметим, что:
  • При операциях Create и Update обновляются все данные, связанные с таблицей Book (уберите Cascade=«save-update» или cascade=«all» и связанные данные не будут сохранены)
  • При удалении удаляются данные из таблиц Book, Mind, Book_Author, а остальные данные не удаляются, потому что у них Cascade=«save-update»

Маппинг для классов, у которых есть наследование.
А как маппить классы у которых есть наследование? Допустим, имеем такой пример:
//Класс Двумерных фигур public class TwoDShape { //Ширина public virtual int Width { get; set; } //Высота public virtual int Height { get; set; } } //Класс треугольник public class Triangle: TwoDShape { //Идентификационный номер public virtual int Id { get; set; } //Вид треугольника public virtual string Style { get; set; } }

В принципе, ничего сложного в этом маппинге нет, мы просто создадим один маппинг для производного класса, то есть таблицы Triangle.
//Маппинг треугольника public class TriangleMap: ClassMap { public TriangleMap() { Id(x => x.Id); Map(x => x.Style); Map(x => x.Height); Map(x => x.Width); } }
После запуска приложения, в БД Biblioteca появится следующая (пустая) таблица

Теги:

  • asp.net mvc 4
  • nhibernate
  • sql server
Добавить метки
Часть 3. Отображение данных из таблицы (Операция LIST)

В предыдущей части были рассмотрены виды связей (один-к-одному, один-ко-многим, многие-ко-многим), а также один класс Book и его маппинг-класс BookMap. Во второй части обновим класс Book, создадим остальные классы и связи между ними, как это было изображено в предыдущей главе в Диаграмме баз данных, расположившейся над подзаголовком 1.3.1 Связи.

Код классов и маппингов (С комментариями)

Класс Книга

Public class Book { //Уникальный идентификатор public virtual int Id { get; set; } //Название public virtual string Name { get; set; } //Описание public virtual string Description { get; set; } //Оценка Мира фантастики public virtual int MfRaiting { get; set; } //Номера страниц public virtual int PageNumber { get; set; } //Ссылка на картинку public virtual string Image { get; set; } //Дата поступления книги (фильтр по новинкам!) public virtual DateTime IncomeDate { get; set; } //Жанр (Многие-ко-Многим) //Почему ISet а не IList? Только одна коллекция (IList) может выбираться с помощью JOIN выборки, если нужно более одной коллекции для выборки JOIN, то лучше их преобразовать в коллекцию ISet public virtual ISet Genres { get; set; } //Серия (Многие-к-одному) public virtual Series Series { get; set; } //Мнение и другое (Один-к-одному) private Mind _mind; public virtual Mind Mind { get { return _mind ?? (_mind = new Mind()); } set { _mind = value; } } //Автор (Многие-ко-многим) public virtual ISet Authors { get; set; } //Заранее инициализируем, чтобы исключение null не возникало. public Book() { //Неупорядочное множество (в одной таблице не может присутствовать две точь-в-точь одинаковые строки, в противном случае выбирает одну, а другую игнорирует) Genres = new HashSet(); Authors = new HashSet(); } } //Маппинг класса Book public class BookMap: ClassMap { public BookMap() { Id(x => x.Id); Map(x => x.Name); Map(x => x.Description); Map(x => x.MfRaiting); Map(x => x.PageNumber); Map(x => x.Image); Map(x => x.IncomeDate); //Отношение многие-ко-многим HasManyToMany(x => x.Genres) //Правила каскадирования All - Когда объект сохраняется, обновляется или удаляется, проверяются и //создаются/обновляются/добавляются все зависимые объекты.Cascade.SaveUpdate() //Название промежуточной таблицы ДОЛЖНО быть как и у класса Genre! .Table("Book_Genre"); HasManyToMany(x => x.Authors) .Cascade.SaveUpdate() .Table("Book_Author"); //Отношение многие к одному References(x => x.Series); //Отношение один-к-одному. Главный класс. HasOne(x => x.Mind).Cascade.All().Constrained(); } }

Public class Author { public virtual int Id { get; set; } //Имя-Фамилия public virtual string Name { get; set; } //Биография public virtual string Biography { get; set; } //Книжки public virtual ISet Books { get; set; } //Инициализация Авторов public Author() { Books=new HashSet(); } } //Маппинг Автора public class AuthorMap: ClassMap { public AuthorMap() { Id(x => x.Id); Map(x => x.Name); Map(x => x.Biography); //Отношение многие-ко-многим HasManyToMany(x => x.Books) //Правила каскадирования All - Когда объект сохраняется, обновляется или удаляется, проверяются и создаются/обновляются/добавляются все зависимые объекты.Cascade.All() //Владельцем коллекции явл. другой конец отношения (Book) и он будет сохранен первым. .Inverse() //Название промежуточной таблицы ДОЛЖНО быть как и у класса Book! .Table("Book_Author"); } }

Класс Жанр

Public class Genre { public virtual int Id { get; set; } //Название жанра public virtual string Name { get; set; } //Английское название жанра public virtual string EngName { get; set; } //Книжки public virtual ISet Books { get; set; } //Инициализация книг public Genre() { Books=new HashSet(); } } //Маппинг жанра public class GenreMap: ClassMap { public GenreMap() { Id(x => x.Id); Map(x => x.Name); Map(x => x.EngName); //Отношение многие-ко-многим HasManyToMany(x => x.Books) //Правила каскадирования All - Когда объект сохраняется, обновляется или удаляется, проверяются и создаются/обновляются/добавляются все зависимые объекты.Cascade.All() //Владельцем коллекции явл. другой конец отношения (Book) и он будет сохранен первым. .Inverse() //Название промежуточной таблицы ДОЛЖНО быть как и у класса Book! .Table("Book_Genre"); } }

Класс Мнение:

Public class Mind { public virtual int Id { get; set; } //Мое мнение public virtual string MyMind { get; set; } //Мнение фантлаба public virtual string MindFantLab { get; set; } //Книга public virtual Book Book { get; set; } } //Маппинг Мind public class MindMap:ClassMap { public MindMap() { Id(x => x.Id); Map(x => x.MyMind); Map(x => x.MindFantLab); //Отношение один к одному HasOne(x => x.Book); } }

Класс Цикл(Серия):

Public class Series { public virtual int Id { get; set; } public virtual string Name { get; set; } //Я создал IList, а не ISet, потому что кроме Book, Series больше ни с чем не связана, хотя можно сделать и ISet public virtual IList Books { get; set; } //Инициализация книг. public Series() { Books = new List(); } } public class SeriesMap: ClassMap { public SeriesMap() { Id(x => x.Id); Map(x => x.Name); //Отношение один-ко-многим HasMany(x => x.Books) ////Владельцем коллекции явл. другой конец отношения (Book) и он будет сохранен первым. .Inverse() } }

Небольшое объяснение
public virtual ISet Genres { get; set; }
public virtual ISet Authors { get; set; }

Почему ISet, а не, к примеру, привычный многим IList? Если использовать вместо ISet - IList, и попробовать запустить проект, то разницы особой мы не заметим (Таблицы и классы создадутся). Но когда мы к классу Book LeftJoin-им одновременно таблицу Genre и Authors, да и еще пытаемся вывести неповторяющиеся записи из таблицы Book (Distinct Book.Id) в представление (View), Nhibernate выдаст исключение и ошибку.
Cannot simultaneously fetch multiple bags.
В таких случаях используем ISet, тем более множества для этого и предназначены (игнорируют дублирующие записи).

Отношение многие-ко-многим.

В NHibernate есть понятие, «главной» таблицы. Хотя отношения «многие-ко-многим» между таблицами “Book” и “Автор” равнозначны (У автора может быть много книг, у книги может быть множество авторов), Nhibernate требует, чтобы программист указывал таблицу, которая сохраняется второй (имеет метод.inverse()), то есть вначале будет создана/обновлена/удалена запись в таблице Book, а только потом в таблице Author.
Cascade.All означает выполнение каскадных операций при save-update и delete. То есть когда объект сохраняется, обновляется или удаляется, проверяются и создаются/обновляются/добавляются все зависимые объекты (Ps. Можно прописать вместо Cascade.All -> .Cascade.SaveUpdate().Cascade.Delete())
Метод.Table(«Book_Author»); создает «промежуточную» таблицу “Book_Author” в БД.

Отношение многие-к-одному, один-ко-многим.

Метод.Constrained() говорит NHibernate, что для записи из таблицы Book должна соответствовать запись из таблицы Mind (id таблицы Mind должен быть равен id таблицы Book)

Если сейчас запустить проект и посмотреть БД Bibilioteca, то появятся новые таблицы с уже сформированными связями.

Далее заполним созданные таблицы данными…
Для этого создадим тестовое приложение, которое будет сохранять данные в БД, обновлять и удалять их, изменив HomeController следующим образом (Ненужные участки кода комментируем):
public ActionResult Index() { using (ISession session = NHibernateHelper.OpenSession()) { using (ITransaction transaction = session.BeginTransaction()) { //Создать, добавить var createBook = new Book(); createBook.Name = "Metro2033"; createBook.Description = "Постапокалипсическая мистика"; createBook.Authors.Add(new Author { Name = "Глуховский" }); createBook.Genres.Add(new Genre { Name = "Постапокалипсическая мистика" }); createBook.Series = new Series { Name = "Метро" }; createBook.Mind = new Mind { MyMind = "Постапокалипсическая мистика" }; session.SaveOrUpdate(createBook); //Обновить (По идентификатору) //var series = session.Get(1); //var updateBook = session.Get(1); //updateBook.Name = "Metro2034"; //updateBook.Description = "Антиутопия"; //updateBook.Authors.ElementAt(0).Name = "Глуховский"; //updateBook.Genres.ElementAt(0).Name = "Антиутопия"; //updateBook.Series = series; //updateBook.Mind.MyMind = "11111"; //session.SaveOrUpdate(updateBook); //Удаление (По идентификатору) //var deleteBook = session.Get(1); //session.Delete(deleteBook); transaction.Commit(); } Genre genreAl = null; Author authorAl = null; Series seriesAl = null; Mind mindAl = null; var books = session.QueryOver() //Left Join с таблицей Genres .JoinAlias(p => p.Genres, () => .JoinAlias(p => p.Authors, () => authorAl, JoinType.LeftOuterJoin) .JoinAlias(p => p.Series, () => seriesAl, JoinType.LeftOuterJoin) .JoinAlias(p => p.Mind, () => mindAl, JoinType.LeftOuterJoin) //Убирает повторяющиеся id номера таблицы Book. .TransformUsing(Transformers.DistinctRootEntity).List(); return View(books); } }

Небольшое объяснение

  1. var books = session.QueryOver() Select * From Book ;
  2. .JoinAlias(p => p.Genres, () => genreAl, JoinType.LeftOuterJoin) - подобно выполнению скрипта SQL:
    SELECT *FROM Book
    inner JOIN Book_Genre ON book.id = Book_Genre.Book_id
    LEFT JOIN Genre ON Book_Genre.Genre_id = Genre.id
  3. .TransformUsing(Transformers.DistinctRootEntity) - Подобно выполнению скрипта SQL: SELECT distinct Book.Id... , (убирает дублирующие записи с одинаковыми id)

Виды объединений
.JoinAlias(p => p.Genres, () => genreAl, JoinType.LeftOuterJoin)

  1. LeftOuterJoin - выбирает все записи из левой таблицы (Book ), а затем присоединяет к ним записи правой таблицы (Genre ). Если не найдена соответствующая запись в правой таблицы, отображает её как Null
  2. RightOuterJoin действует в противоположность LEFT JOIN - выбирает все записи из правой таблицы (Genre ), а затем присоединяет к ним записи левой таблицы (Book )
  3. InnerJoin - выбирает только те записи из левой таблиц (Book ) у которой есть соответствующая запись из правой таблицы (Genre ), а затем присоединяет к ним записи из правой таблицы

Изменим представление следующим образом:

Представление index

@model IEnumerable @{ Layout = null; } Index

@Html.ActionLink("Create New", "Create")

@foreach (var item in Model) { @{string strSeries = item.Series != null ? item.Series.Name: null;} }
@Html.DisplayNameFor(model => model.Name) @Html.DisplayNameFor(model => model.Mind) @Html.DisplayNameFor(model => model.Series) @Html.DisplayNameFor(model => model.Authors) @Html.DisplayNameFor(model => model.Genres) Операции
@Html.DisplayFor(modelItem => item.Name) @Html.DisplayFor(modelItem => item.Mind.MyMind)@Html.DisplayFor(modelItem => strSeries) @foreach (var author in item.Authors) { string strAuthor = author != null ? author.Name: null; @Html.DisplayFor(modelItem => strAuthor)
}
@foreach (var genre in item.Genres) { string strGenre = genre!= null ? genre.Name: null; @Html.DisplayFor(modelItem => strGenre)
}
@Html.ActionLink("Edit", "Edit", new { id = item.Id }) | @Html.ActionLink("Details", "Details", new { id = item.Id }) | @Html.ActionLink("Delete", "Delete", new { id = item.Id })


Проверив поочередно все операции, мы заметим, что:
  • При операциях Create и Update обновляются все данные, связанные с таблицей Book (уберите Cascade=«save-update» или cascade=«all» и связанные данные не будут сохранены)
  • При удалении удаляются данные из таблиц Book, Mind, Book_Author, а остальные данные не удаляются, потому что у них Cascade=«save-update»

Маппинг для классов, у которых есть наследование.
А как маппить классы у которых есть наследование? Допустим, имеем такой пример:
//Класс Двумерных фигур public class TwoDShape { //Ширина public virtual int Width { get; set; } //Высота public virtual int Height { get; set; } } //Класс треугольник public class Triangle: TwoDShape { //Идентификационный номер public virtual int Id { get; set; } //Вид треугольника public virtual string Style { get; set; } }

В принципе, ничего сложного в этом маппинге нет, мы просто создадим один маппинг для производного класса, то есть таблицы Triangle.
//Маппинг треугольника public class TriangleMap: ClassMap { public TriangleMap() { Id(x => x.Id); Map(x => x.Style); Map(x => x.Height); Map(x => x.Width); } }
После запуска приложения, в БД Biblioteca появится следующая (пустая) таблица

Теги: Добавить метки

Дорогие друзья!

Сегодня мы рады сообщить вам о том, что наши разработчики реализовали возможность транспортировки данных по URL (mapping, мэппинг) за “пределы” целевой страницы.

С помощью данной функции можно передать все данные с полей формы на страницу, на которую переходит пользователь, отправляя вам лид. Благодаря этому, лид попадает не только во , но также может попасть сразу и в вашу базу, если на странице редиректа его “встретит” соответствующий скрипт.

Теперь нет необходимости в экспорте данных из системы обработки лидов! Вы можете отправлять и обрабатывать их сразу в собственной базе!

Так же, благодаря данной функции, становится возможным поздравить или поблагодарить пользователя, предоставившего свои контактные данные лично.

Как работает мэппинг (mapping)?

Суть мэппинга в том, что при отправке данных с полей формы, в ссылку, по которой происходит переадресация, добавляются их содержимое. URL приобретает вид: //my_site.com/?name=ИМЯ&email=АДРЕС_ЭЛЕКТРОННОЙ _ПОЧТЫ&phone=НОМЕР_ТЕЛЕФОНА&lead_id=225298.

Важно! В дополнение ко всем данным полей, всегда передается ID лида в параметре lead_id.

На странице же, на которую осуществоляется переход, данную информацию “принимает” специальный скрипт, который, в свою очередь, распределяет данные по соответствующим “ячейкам”.

Обращаем ваше внимание! Мэппинг работает только в том случае, если “Результат формы” - “Переход по URL”!

Как настроить “транспортировку” лида по URL (mapping) на моей целевой странице?

1. Войдите в .
2. Выберите страницу с формой лида, с которой вы собираетесь “транслировать” данные.

3. В редакторе сделайте двойной щелчок по форме.

4. В появившемся окне заполните графу “Mapping” соответствующими названиями полей на английском языке. Например,name - имя, phone - телефон и т. п.

5. Сохраните изменения.

6. В свойствах формы настройте редирект на нужную страницу - это может быть или страница вашего сайта, в которую встроен JavaScript, который и будет обрабатывать данные с полей, поступившие с URL.

Установите флажок в чекбоксе “Передать поля формы”.

7. Сохраните изменения в основном меню редактора.

Вот и все! :-)

Теперь данные с полей вашей формы будут передаваться на страницу, на которую вы переадресовываете пользователя. Вам не придется экспортировать лиды из CRM LPgenerator - они могут “транспортироваться” в вашу CRM прямо по URL. Возможности мэппинга (mapping) по транспортировке данных воистину безграничны.

В данной статье мы хотели бы систематизировать наш опыт проведения миграции данных в крупных корпоративных проектах, связанных с переходом Заказчиков на работу в конфигурациях «1С:Предприятие 8».

При этом основной акцент в статье будет сделан, прежде всего, на технологическую составляющую процесса миграции. Организационная составляющая также затронута, но в меньшей степени.

Термины и определения

Под миграцией данных принято понимать конечную последовательность работ, проект, направленный на разовое массовое перемещение данных из систем-источников (исторические системы) в систему-приёмник. При этом эксплуатация этих данных в системах-источниках прекращается.

Следует отличать миграцию данных от интеграции данных. Интеграция, в отличие от миграции - это постоянная часть архитектуры IT, и ответственна за потоки данных между различными системами и хранилищами данных - и является процессом, а не деятельностью по осуществлению проекта.

Схема миграции в общем случае выглядит следующим образом:

Рис. 1

Исторические системы - базы данных компании Заказчика, которые планируется полностью или частично заменить при внедрении новой системы.

Система-приёмник - целевая система, произвольная конфигурация «1С:Предприятие 8».

Исходные данные - данные, выгруженные из исторических систем в произвольный формат xls -файлов. В данном случае формат xls представляется, как один из самых удобных, поскольку возможность выгрузки в xls -файл присутствует во многих учетных системах «предыдущих поколений».

Как современную альтернативу в качестве транспорта возможно рассматривать формат xml -файлов.

Также существуют варианты использования промежуточной базы данных.

Трансформация, конвертация - процесс преобразования исходных данных в данные для загрузки. Трансформация данных происходит в соответствии с шаблонами для загрузки. Результатом трансформации являются данные для загрузки.

Данные для загрузки - данные, предназначенные для загрузки в систему-приёмник. В данной статье, так же как и исходные данные, рассматривается xls -формат.

Шаблоны данных для загрузки - описание таблиц данных для загрузки в целевую систему.

Этапы миграции

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

К организационным этапам миграции можно отнести следующие пункты:

· Определение стратегии миграции. На данном этапе Исполнитель и Заказчик договариваются о технологии проведения миграционных работ;

· Определение состава рабочей группы по миграции. В рабочую группу должны входить специалисты и Исполнителя и Заказчика, знакомые в достаточной степени с работой исторических систем (со стороны Заказчика) и целевой системы (со стороны Исполнителя);

· Предварительный план миграции. План миграции по ходу проекта будет неоднократно корректироваться;

· Периоды дат выгрузки данных из исторических систем, объемы данных. Периоды среза данных для миграций, даты тестовых и итоговой миграций. Данную информацию можно отнести к плану миграции;

· Состав данных, подлежащих миграции. Справочные данные, классификаторы, транзакционные данные, остатки, обороты и пр.;

· Вопросы проверки качества, корректности и целостности данных в процессе миграции и по итогам;

· Вопросы отката к предыдущему состоянию в случае сбоев.

Остановимся подробнее на технологических этапах миграции.

Рис. 2

1.Подготовка шаблонов загрузки данных

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

Каждый шаблон в общем случае предназначен для одной или нескольких связанных таблиц в целевой системе-приёмнике.

В шаблоне указывается:

· Описание всех полей xls -файла данных для загрузки, включая:

o Имя поля

o Признак обязательности заполнения поля

o Пример заполнения поля

o Примечание

· Описание правил загрузки таблицы целевой системы на основании данных для загрузки (очередность в случае нескольких связанных таблиц, алгоритмы поиска по ключевым полям и т.п.)

· Описание заполнения непосредственно полей таблиц целевой системы в случае, если предусматривается что-либо отличное от переноса данных «один в один» из файла данных для загрузки. Актуально для ссылочных полей, например.

В процессе работ по данному этапу Исполнитель также должен подготовить загрузчик файлов данных для загрузки. В случае работы с файлами xls данная задача не представляет особой сложности.

2.Выявление источников данных

Данный этап может начинаться вместе с предыдущим этапом «1. Подготовка шаблонов загрузки данных».

В рамках данного этапа специалисты Заказчика определяют из каких систем и какие данные могут быть выгружены. Также следует определить какие данные возможно могут понадобиться.

Как правило, в больших проектах миграции выявление полного исчерпывающего списка источников данных может занимать достаточно продолжительное время и происходит по мере работ на последующих этапах.

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

Тем не менее, на данном этапе нужно постараться выявить как можно больше необходимых данных.

3.Выгрузка исходных данных

Процесс выгрузки данных из исторических систем может занять достаточное количество времени, особенно, если систем много, они разные и за них ответственны разные подразделения Заказчика. Необходимо учитывать данный момент при тестовых и итоговой миграциях.

Наиболее удобным вариантом представляется выгрузка в xls файлы. Многие старые IT -системы поддерживают такой вариант.

Также могут быть варианты выгрузки в csv формат, dbf , xml форматы и прочие.

Стоит отметить, что по тем или иным причинам (вопросы безопасности, например) Заказчик не всегда может предоставить выгрузки данных в полном объеме на этом этапе! Только структура данных и несколько тестовых позиций. Таким образом, может сложиться такая ситуация, что при тестовых и итоговой загрузках будут обнаруживаться некачественные данные в исходных таблицах, что будет приводить к незапланированным ошибкам.

Для минимизации данной проблемы следует оговорить заранее объемы тестовых выгрузок из исторических систем.

4.Мэппинг данных

Мэппинг (data mapping ) - в общем случае процесс сопоставления данных исторических систем и системы-приемника. То есть, исходных данных и данных для загрузки.

Этап мэппинга - наиболее трудоёмкий этап и может занимать более 50% всех работ по задаче миграции.

На данном этапе в полной мере задействуется вся рабочая группа проекта по миграции.

В процессе мэппинга данных необходимо выделить подэтапы мэппинга таблиц и мэппинга полей.

· Мэппинг таблиц, или мэппинг шаблонов - сопоставление таблиц исходных данных и шаблонов данных для загрузки. Соответствие может быть как 1:1, так и N :N . В результате данной работы составляется и поддерживается реестр мэппинга таблиц. Данный подэтап необходим для следующего подэтапа мэппинга полей и для отслеживания общего состояния дел по мэппингу.

Группа шаблонов 1С

Наименование шаблона 1С

Наименование файла-

источника

Правила формирования файла-источника

Ответственный

Статус

Примечание

НСИ

Шаблон_

Номенклатура

Номенк

латура.xls

В системе N установить отбор
. Сохранить в txt
. Открыть в xls, колонки - текстовые
. Первая строка - шапка
. Кол-во столбцов - 15
. Сверить кол-во строк в txt и xls
. Наименование листа всегда "Лист1"

Иванов И.И.

в работе

· Мэппинг полей - сопоставление полей таблиц в рамках уже определенного мэппинга таблиц. Результатом данной работы является реестр мэппинга полей.

№пп

Кл. поле

Обязательный

Имя поля шаблона 1С «Шаблон_Номенклатура»

Описание

Имя поля «Номенклатура.xls»

Алгоритм заполнения

Код

Код элемента справочника

Код

Наименование

Наименование

Да

Это группа

Содержит одно из значений:
. 1 - для групп
. 0 - для элементов

Если длина кода=11 символов и последние 4 символа <> "0000", то это элемент - "0", иначе группа - "1".

Полное наименование

Наименование элемента справочника

Наименование

Если ЭтоГруппа =1 , То "", ИначеЕсли ЭтоГруппа=0, то Наименование.

В рамках данного этапа также следует провести возможные работы по нормализации данных.

5.Подготовка правил трансформации

В отличие от предыдущих этапов, данный этап - технический и предполагает работу разработчика Исполнителя.

На основании согласованных реестров мэппинга полей специалисты Исполнителя разрабатывают правила трансформации данных.

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

При этом требования к данной среде включают в себя:

· Удобство и быстрота разработки правил трансформации;

· Скорость конвертации данных. Файлы на входе и на выходе могут быть и в сотни тысяч строк!

· Возможность работать с несколькими входными файлами одновременно;

· Возможность сохранения правил трансформации в отдельные файлы.

Для своих проектов миграции мы разработали специализированное АРМ разработчика, взяв за основу стандартную обработку «Консоль запросов» 1С.

Обработка «Консоль запросов» была доработана для возможности делать прямые запросы к файлам xls .

Приведем пример объединения двух исходных xls -файлов Сотрудники. xls


Код сотрудника

Фамилия

Имя

Отчество

Дата рождения

2423

Иванов

Иван

Иванович

17.11.1992

1523

Петров

Василий

Александрович

04.02.1991

4363

Сидоров

Кирилл

Николаевич

01.05.1995

Денисов

Денис

Денисович

01.01.1990

и Операции. xls со страницами:

Списания

Код сотрудника

Дата

Сумма

2423

01.02.2014

1523

02.02.2014

4363

03.02.2014

04.02.2014

100000

2423

05.02.2014

1523

06.02.2014

4363

07.02.2014

2356

08.02.2014

140000

2423

09.02.2014

1523

10.02.2014

4363

11.02.2014

23523

12.02.2014

80000

и Поступления :

Код сотрудника

Дата

Сумма

01.05.2004

02.05.2004

03.05.2004

04.05.2004

2423Дата рождения

Сумма поступление

Сумма списание

Иванов Иван Иванович

2423

17.11.1992

1341234

1010

Петров Василий Александрович

1523

04.02.1991

245245

Денисов Денис Денисович

01.01.1990

380000

320000

Сидоров Кирилл Николаевич

4363

01.05.1995

613382

26336

ИТОГО:

2579861

347842

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

Технологическая последовательность операций трансформации здесь выглядит следующим образом:

С помощью языка запросов Access SQL (дающего существенные дополнительные возможности, по сравнению с языком запросов 1С) создается первоначальный запрос, извлекающий данные из файла xls в среду 1С. При этом уже на данном этапе возможны различные проверки и нормализации данных.

Технология доступа к данным ADO обеспечивает высокую скорость работы.

Рис. 3

2.Запрос на языке 1С - основной запрос, реализующий алгоритм мэппинга полей. А также: обогащение загружаемых данных данными из базы 1С, перегруппирование, объединение с результатами запросов к другим исходным xls -файлам и пр.

3.Постобработка результата запроса 1С при необходимости. Реализуется с помощью скрипта на языке 1С.

Для примера здесь реализуется добавление строки «ИТОГО» по колонкам сумм.

4.Запись итогового набора данных в xls -файл.

В общем случае на выходе мы получаем итоговые файлы для загрузки в целевую базу данных 1С.

Также данный инструмент позволяет сохранять правила конвертации данных в отдельный xml файл:

Кроме того, реализована возможность работать в пакетном режиме , что особенно актуально при большом количестве разнородных мигрирующих данных.

В ходе предыдущих этапов подготовительная часть работы в целом заканчивается - выявлены все источники данных, сделана выгрузка исходных данных из источников, подготовлены шаблоны загрузки в целевую базу, подготовлен мэппинг данных и, наконец, разработаны скрипты трансформации данных.

Следует отметить, что перед итоговой миграцией обязательно следует провести несколько тестовых. В ходе тестовых миграций Исполнитель совместно с Заказчиков выявляют:

· ошибки конвертации, ошибки загрузки данных

· проводят предварительную оценку качества загружаемых в целевую систему данных

· по итогам тестовых миграций составляют/актуализируют план итоговой миграции

7.Выверка данных

Проверка качества загруженных данных должна производиться как после тестовых миграций, так и по окончанию итоговой миграции. В ходе выверки могут проверяться следующие показатели:

· Совпадения итоговых сумм по остаткам, по документам;

· Количественные совпадения, например количество ОС;

· Корректность заполнения отдельных выборочных сущностей;

Обращаем внимание, что те или иные проверки мигрирующих данных, вопросы нормализации данных необходимо решать на протяжении всех миграционных процессов. Необходимо всегда задаваться вопросом, что нужно сделать на текущем этапе, чтобы избежать ошибок на последующих этапах.

Например:

· Проверка на дубли по ключевым полям. Можно и нужно проводить еще на исходных данных;

· Приведение типов полей;

· Ссылочная целостность;

· Математические нестыковки. Например, проверка на незаполненные численные поля, на которые запланировано деление при трансформации;

· В целом, проверки обязательной заполненности полей;

· Замена некорректных символов. Например, английские символы в кириллических полях («о», «а», «е» и т.п.) Особенно актуально это для ключевых полей!

· Проверка значений строковых полей на соответствие типов системы-приемника (Ограничения по длине)

После завершения итоговой миграции согласно заранее определенной стратегии миграции и плану миграции принимается решение о дальнейшей эксплуатации исторических систем.

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

В некоторых случаях может происходить параллельная работа двух систем на время опытной эксплуатации (ОЭ) и даже более этого периода. Вопрос параллельной работы пользователей в двух системах тесно связан с вопросом возможности отката к старой системе, в случае если миграция (или же, в целом, работа новой системы!) будет признана неудовлетворительной.

Заключение

В заключении хотелось бы отметить, что когда речь идёт о миграции больших транзакционных систем, к которым относятся и многие конфигурации «1С:Предприятия», переход на новую систему может быть весьма трудоёмким.

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

Хранилище данных для финансового учреждения

Хранилища данных в банковской отрасли призваны консолидировать разрозненные данные разобщённых систем и извлекать информацию из консолидированных данных. Хотя хранилище может решать проблемы консолидации данных, оно не может решить как по волшебству все проблемы, связанные с информаций. Формирование проекта по построению банковского хранилища данных и управление им требует сознательных усилий всех заинтересованных сторон.

В этой связи стоит взглянуть на шаги (которые могут быть осуществлены даже до внедрения модели данных), обеспечивающие создание идеальной среды для успешного внедрения хранилища данных в банке.

1. Определение заинтересованных сторон в соответствии с бизнесом банка (розничные банковские услуги, корпоративный бизнес, кредитные карты, и т. п.).

Заинтересованные стороны должны обладать соответствующими знаниями. Для лучшего понимания хранилища данных и его потребностей для каждой отдельной бизнес-функции необходимо включать в проектную группу сотрудников как со стороны бизнеса, так и со стороны подразделения бизнес-технологии. Заинтересованные стороны должны быть вовлечены в проект с самого начала, чтобы избежать утечек информации в группах.

2. Обучающие занятия для понимания потребности в хранилище данных в банке.

Заинтересованные в проекте группы должны понимать, что хранилище данных - это репозиторий только для релевантных элементов данных, а не точная копия исходной системы. Это помогает группам решить, какие данные стоит хранить.

3. Понимание концепции моделирования данных.

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

4. Коллективное выявление ландшафта исходных систем.

Следует убедиться, что каждой системе в организации уделено должное внимание, и рассмотрен вопрос о её включении в хранилище данных.

5. Выстраивание проекта вокруг базовой модели для понимания общего подхода к расширению модели данных.

Базовая модель должна покрывать основные измерения в масштабе бизнеса и давать представление о фактических данных, которые, возможно, надо хранить.

6. Мэппинг данных .

a) Мэппинг данных из источника (исходных систем в организации) в целевую структуру (модель данных хранилища).

Необходимо определить исходные системы и отношения между системами для каждого измерения в модели данных.

b) Мэппинг данных для каждой функции с членами группы со стороны бизнеса и ИТ:

Этот мэппинг может потребоваться на двух уровнях:

Прямой мэппинг из исходных систем: большинство элементов данных будут отнесены напрямую в модель данных. Здесь потребуется определить источник и наименования полей.

Производный мэппинг из исходных систем: некоторые элементы данных в модели могут потребовать применения бизнес-правил к данным исходной системы для получения точной информации. Они должны быть чётко документированы.

При мэппинге из двух и более источников следует определить отношения между этими исходными системами.

7. Определение агрегирования .

Одна из целей создания хранилища данных - это получение аналитической информации из исторических данных. Кроме того, это включает в себя построение предикативных тенденций на основе данных. Агрегация определяет слои или измерения, в разрезе которых анализируются данные. Лучше всего, если агрегации определены на основании информации (отчётов и информационных панелей), которая должна быть получена из модели данных.

8. Пропускание и именование элементов данных.

Проектные команды должны принять тот факт, что источник не всегда может предоставить все данные в рамках стандартной модели. Члены команды должны принять решение либо исключить эти элементы в модели или оставить их, но не использовать. Предпочтительный путь – это опустить нерелевантные поля. Они должны оставаться неиспользуемыми, только тогда, когда предполагается, что они могут пригодиться в будущем.

9. Декларация дальнейшего совершенствования процессов .

В то время как хранилища данных сами по себе не являются проектом по усовершенствованию процессов, они могут нести улучшение вне самого хранилища. Недостатки, замеченные в процессах или в исходных системах, должны быть отмечены и параллельно устранены. Однако, изменения, внесённые процессом, могут быть сделаны позже, и не должны затронуть проект построения хранилища данных.

10. Выравнивание версий .

Мэппинг и модели данных должны быть приняты как версии, привязанные к изменениям по мере развития проекта. Важно сформировать процесс, направленный на адаптацию к этим изменениям.

Наличие слишком большого объема данных для хранения и анализа может представлять собой проблему для каждой организации, особенно когда это приводит к противоречиям в показателях. Когда данных становится слишком много, приходится разбираться с конфликтующими данными в отчётах, выбирать между противоречивыми показателями и удалять дублированные записи. Это отнимает слишком много времени и ресурсов, особенно в крупнейших компаниях, внедривших слишком много хранилищ или витрин данных, дающих разную информацию об одинаковых бизнес-процессах или событиях.

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

Сокращение многочисленных хранилищ данных до одного экземпляра

Компания Boeing прошла через этот процесс, начав с 12 хранилищами данных и 50 системами управления расходами, некоторые из которых имели десятки тысяч бизнес-правил. «Проблема заключалась в том, что наш IT-отдел предоставлял пользователям то, что им нужно, однако они не общались друг с другом», - говорит Билл Керли (Bill Curley) сотрудник финансового отдела Boeing. Это отсутствие интеграции являлось причиной несоответствия в отчетах.

У Boeing ушло несколько лет на консолидацию всей финансовой отчётности в единое целое. Работавшие над этой задачей члены проектной группы предпочли подход «сверху вниз» - они опросили «владельцев данных», какая информация им необходимо для выполнения работы, и внедрили стандартный словарь данных, имеющий минимум необходимых элементов. Кроме того, они разделили операционные и фактические бухгалтерские данные, необходимые для отчётов. «Нам больше не требовалось проводить оперативную информацию через нашу бухгалтерскую систему», - говорит Билл Керли.

Переход к использованию единой архитектуры данных для улучшения качества данных

Над этой задачей в течение нескольких последних лет работала компания Nike. Для этого архитектор данных компании Джеймс Ли (James Lee) устранил дублирующиеся данные, заполнил отсутствующие поля в таблицах и разобрался с серией отчётов, которые слишком долго формировались. «На пути к единой версии правды мы хотели достигнуть значительной гибкости, чтобы бизнес-подразделения могли генерировать свои отчёты без участия IT-отдела. Одной из целей была самодостаточность пользователей относительно работы с данными», - вспоминает он. Одна из наиболее часто используемых таблиц Nike содержала больше сотни столбцов. Это было чрезвычайно неэффективно с точки зрения операций ввода-вывода и использования вычислительных мощностей. Nike упростил эту сверхширокую таблицу и сократил свои модели данных до меньшего числа элементов. Этот процесс также улучшил качество данных, так как на пользователей возложили ответственности за отсутствующие, но необходимые элементы данных, и они стали более активными в их отслеживании.

Публикации

  1. Аарти Няядиш (Aarti Nyayadish). «Идеальный проект хранилища данных: 10 шагов для установления верного темпа» (Ideal Banking Data Warehousing project: 10 steps for setting the right pace), 15 января 2013 г.
  2. Дэвид Стром (David Strom). «Как справиться с избытком данных: как это сделали Boeing, Nike и другие» (Coping with Too Much Data: How Boeing, Nike and Others Did It), 23 октября 2012 г.