Полезное для программистов:

Фриланс
Новости
Статьи
   
Рубрики:


Nhibernate & GenericDao. Как быстро начать работать.

Поиск:
Всем привет. Что такое GenericDao, вы должны знать, но если не знаете:
DAO( сокр. от англ. Data Access Objects) - объекты для доступа к данным 
Generic означает что мы будем использовать шаблоны классов (generic), доступные в C#,  для создания такого интерфейса, который позволит работать со всеми нашими классами NHibernate.

1. Создание интерфейса GenericDao

Итак приступим. В прошлой статье я говорил как настроить NHibernate. Какой должен быть файл настроек приложения и т.д.
Для начала рассмотрим то, как же происходит работа с NHibernate (далее хибер, хибернейт), для доступа к БД.
Разберем класс, который нам позволяет настроить NHibernate для работы с нашими объектами, этот класс мы просто рассмотрим, нигде использовать его не будем:
Код

public class DataController
{

private ISessionFactory _sessions;

public DataController()
{
Configuration cfg = new Configuration();
cfg.AddClass(typeof (DataElement));//Указание классов хибера
cfg.AddClass(typeof(Pins));
cfg.AddClass(typeof (Properties));
cfg.AddClass(typeof(Library));
_sessions = cfg.BuildSessionFactory();//Создание фабрики сессий

}
}

В конструкторе мы видим что создается объект класса Configuration, который нам следует сконфигурировать определенным образом. Мы указываем какие хибер классы мы имеем. После этого строим фабрику сессий. После чего фабрика сессий позволяет нам открывать сессию для работы именно с теми классами которые мы указали. Далее рассмотрим как же нам, скажем, сохранить  данные в БД...
Код

public DataElement SaveElement(DataElement el)
{
ISession session = _sessions.OpenSession();//Октрываем сессию
try
{
session.Save(el);              //Сохраняем элемент
return el;                                    //Возвращаем его, для получения его PK, если PK у нас генерируется

}
catch (HibernateException e)
{
throw e;
}
finally
{
session.Close();//закрываем сессию
}
}

Вот так вот просто мы можем работать с объектами класса DataElement. Я думаю тут все понятно что происходит. Но получается, что такие методы нам нужно писать для всех классов... Желания у всех нет писать это, тем более если классов очень много. Не забываем про методы Insert, Update, Delete. Итого у нас четыре хибер класса, для каждого минимум по четыре метода, итого 16 методов. Лень...
Вот тут приходит на помощь DAO + generic.
Итак давайте попробуем создать интерфейс общий для всех хибер объектов, словом *type* мы пометим те типы которые являются хибер классами, т.е. это может быть любой из используемых у нас хибер классов. В моей БД, у всех таблиц PK - это int поле.
Код

public interface IGenericDao
{
*type* Get(int id);
*type* Save(*type*obj);
*type* SaveOrUpdate(*type* obj);
void Delete(*type* obj);
}

Мы видим что для всех классов требуется четыре метода (имеется ввиду минимум)... Удивительно... Но как же нам передавать грамотно тип? А вот так:
Код

public interface IGenericDao<T, ID>
{
T Get(ID id);
T Save(T obj);
T SaveOrUpdate(T obj);
void Delete(T obj);
}

Здесь мы начинаем использовать шаблон класса. Даже два. Первый (T) это шаблон для хибер класса, второй(ID) это шаблон для типа которым является PK. Т.е. мы сможем брать поля из БД, не только по PK  с типом int, но и скажем string.
Вот мы создали интерфейс GenericDao, теперь надо создать класс который будет реализовывать этот интерфейс. Но для начала вспомним о сессиях.

2. Создание фабрики сессий

Мы помним что для того что бы взять, сохранить, обновить или удалить данные из БД, нам надо открыть сессию. И еще замечу, что если мы взяли данные, передали их в объект хибер класса, и закрыли сессию, то если у нас отключено Lazy (по умолчанию отключено), данные в объекте будут не доступны. Из за чего это? Из за того что прокси класс ссылается через сессию на требуемые нам данные (что такое прокси класс, читай документацию к хиберу). После закрытия сессии, получается что прокси класс ссылается на недоступную нам ссылку. Т.е. Получается что сессию мы должны хранить. Как же это сделать? Сессия описывается через интерфейс ISession. И берем мы сессию из фабрики сессий. Как же нам красиво это сделать? А вот как:
Код

public class SessionFactory
{
public static void Init() //Инициализация фабрики сессий
{
Configuration cfg = new Configuration();
cfg.AddAssembly("Win.Objects"); //Конфигурируем NHibernate. Здесь мы указываем на сборку, в которой хранятся мои хибер классы.
sessionFactory = cfg.BuildSessionFactory();
}
private static ISessionFactory sessionFactory; //Объект фабрики сессий, реализованный в хибере
private static ISession threadSession //Сама сессия
{
get
{
return (ISession)CallContext.GetData("THREAD_SESSION"); //Сессию мы храним в контексте, вот так работать с контекстом
}
set
{
CallContext.SetData("THREAD_SESSION", value);
}
}

public static ISession GetSession() //Метод возвращающий нам сессию.
{
ISession session = threadSession; //Берем сессию из контекста

if (session == null) //Смотрим "метрва" ли она
{
session = sessionFactory.OpenSession(); //Через фабрику сессий открываем сессию
threadSession = session; //Записываем ее в контекс
}

return session; //Возвращаем
}
}

Вот так все просто. Получается что сессия хранится в контексте, когда она нам нужна, мы вызываем метод GetSession(), там проверяется "Жива" ли сессия или нет, если нет - создаем новую, если да - возвращаем "живую". Что касается инициализации, то ее надо вызвать один раз в любом месте своей программы, но до того как начали использовать сессию. Скажем в конструкторе главной формы. Вот и все. Разобрались... Теперь посмотрим на реализацию интерфейса IGenericDao.

3. Реализация IGenericDao

У нас есть интерфейс, через который мы реализуем DAO, у нас есть класс для работы с сессией, теперь нам осталось написать методы CRUD(Create, Read, Update, Delete) для работы через generic. Так это происходит:
Код

public class GenericImpl<T, ID> : IGenericDao<T, ID> //Реализуем интерфейс IGenericDao
{
private ISession session //Здесь метод на взятие сессии
{
get
{
return SessionFactory.GetSession(); //Используем нашу фабрику сессий.
}
}

private Type _type = typeof (T); //Тип хибер класса, с которым работаем.

public T Get(ID id) //Метод взятия данных
{
try
{
T result = (T) session.Load(_type, id); //Говорим что возвращаем тип T и загружаем его используя сессию через метод Load
return result; //Возвращаем
}
catch (HibernateException e)
{
throw e;
}
}

public T Save(T obj)
{
try
{
session.Save(obj);
return obj;
}
catch (HibernateException e)
{
throw e;
}
}

public T SaveOrUpdate(T obj)
{
session.SaveOrUpdate(obj);
return obj;
}

public void Delete(T obj)
{
session.Delete(obj);
}
}

И опять все до смешного просто... Теперь мы можем создавать GenericDao для любого нашего хибер класса. Теперь мы можем использовать класс GenericImpl для работы с любым нашим объектом. Как это сделать? Вот так:
Код

GenericImpl<Library> libdao = new GenericImpl<Library>(); //Вот инструмент для работы с объектами хибер класса Library
Library lib = new Library(); //Создаем объект хибер класса
lib.Name = "Новая библиотека"; //Заполняем
libdao.Save(lib);//Так сохраняем
libdao.Get(1);//берем
libdao.Delete(lib);//Удаляем
libdao.SaveOrUpdate(lib);//Обновляем

Ну красиво, неправда ли? Но на этом мы не остановимся. Мне кажется что вам может не понравится то как создается DAO. Попробуем облегчить себе жизнь. Как? Создадим фабрику DAO для каждого хибер класса.

4. Фабрика DAO

Что мы будем делать? Мы создадим для каждого класса свое DAO, уникальное, но при этом мы не напишем больше ни одного метода CRUD. Как так сделать? Просто. Но по порядку...
Мы создадим интерфейс DAO для каждого хибер класса, просто наследуя его от базового IGenericDao, но с указанием того с каким хибер классом мы будем работать:
Код

public interface IDataElementDao : IGenericDao<DataElement,int>{}
public interface ILibraryDao : IGenericDao<Library, int> { }
public interface IPinsDao : IGenericDao<Pins, int> { }
public interface IPropertiesDao : IGenericDao<Properties, int>{ }

Минус конечно что для каждого класса надо объявлять свой интерфейс, но зато не надо писать CRUD методы. Дальше мы сделаем интерфейс для фабрики DAO, которая будет нам поставлять уже готовые интерфейсы Dao для каждого хибер класса:
Код

public interface IDaoFactory
{
IDataElementDao GetDataElementDao();
ILibraryDao GetLibraryDao();
IPinsDao GetPinsDao();
IPropertiesDao GetPropertiesDao();
}

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

public class HDataElement : GenericImpl<DataElement,int> IDataElementDao{}
public class HLibrary : GenericImpl<Library,int> ILibraryDao{}
public class HPins : GenericImpl<Pins,int> IPinsDao{}
public class HProperties : GenericImpl<Properties, int> IPropertiesDao{}

Получается что мы должны реализовать интерфейс DAO для каждого хибер класса, и мы реализуем его наследовав базовый GenericImpl с указанием хибер класса. И реализуем фабрику DAO:
Код

ublic class FactoryDao : IDaoFactory
{
public IDataElementDao GetDataElementDao()
{
return new HDataElement();
}

public ILibraryDao GetLibraryDao()
{
return new HLibrary();
}

public IPinsDao GetPinsDao()
{
return new HPins();
}

public IPropertiesDao GetPropertiesDao()
{
return new HProperties();
}
}

Вот так мы получаем готовые объекты DAO для работы с любым хибер классом. Как этим пользоваться? Вот так:
Код


IDaoFactory fact = new FactoryDao(); //Создаем фабрику дао

ILibraryDao libdao = fact.GetLibraryDao();//берем дао для Library
Library lib = new Library();
lib.Name = "Новая библиотека";
libdao.Save(lib);
libdao.Get(1);
libdao.Delete(lib);
libdao.SaveOrUpdate(lib);

IDataElementDao eldao = fact.GetDataElementDao();//Берем ДАО для DataElement и можем дальше работать с ним.


ЗАКЛЮЧЕНИЕ

Хочется добавить что если есть не решенные вопросы, то озвучивайте их и тогда может быть ваш вопрос попадет в тему следующих статей.
Для тех кому и этого мало, то идем
[url=http://www.ibm.com/developerworks/ru/library/j-genericdao/>СЮДА[/url]. Здесь описано как еще больше облегчить наши проблемы с использованием Spring FrameWork и его SpringAOP. Вещь крайне полезная, но очень прихотливая.
Ну вот и все. Надеюсь всем все понятно. Претензии как всегда все принимаются и будут услышаны. Надеюсь пригодится.)
Автор: Куповых Вадим






Просмотров: 7231

 

 

Новые статьи:


Популярные:
  1. Как сделать цикличным проигрывание MIDI-файла?
  2. Создание AVI файла из рисунков
  3. Как устройство "отключить в данной конфигурации"?
  4. Kто в данный момент присоединен через Сеть?
  5. Как узнать количество доступной памяти?
  6. Как реализовать в RichEdit разноцветный текст?
  7. Как скрыть свое приложение от ProcessViewer
  8. Как программно нажать/скрыть/показ кнопку "Start"?
  9. Модуль работы с ресурсами в PE файлах
10. Функции вызова диалоговых окон выбора
11. Проверка граматики средствами Word'а из Delphi.
12. Модуль для упрощенного вызова сообщений
13. Функции для записи и чтение своих данных в, ЕХЕ- файле
14. Рекурсивный просмотр директорий
15. Network Traffic Monitor
16. Разные модули
17. Универсальная функция для обращения к любым экспортируем функциям DLL
18. Библиотека от VladS
19. Протектор для UPX'а
20. Еще об ICQ, сообщения по контакт листу?
21. Использование открытых интерфейсов
22. Теория и практика использования RTTI
23. Работа с TApplication
24. Примеры использования Drag and Drop для различных визуальных компонентов
25. Что такое порт? Правила для работы с портами
26. Симфония на клавиатуре
27. Загрузка DLL
28. Исправление автоинкремента
29. Взаимодействие с чужими окнами
30. Проверить дубляжи в столбце


 

 

 
 
На главную