Nhibernate. DAO + Generic
Hello. What is GenericDao, you should know, but if you do not know:
DAO (abbr. From English. Data Access Objects) - objects for accessing data.
Generic means that we will use the class templates (generic) available in C # to create an interface that will work with all our NHibernate classes.
So let's get started. In a previous article, I talked about how to configure NHibernate. What should be the application settings file, etc.
First, let's look at how the work with NHibernate (hereinafter referred to as hibernate) occurs to access the database.
Let's analyze the class that allows us to configure NHibernate to work with our objects, we just look at this class, we will not use it anywhere:
In the constructor, we see that an object of the Configuration class is created, which we need to configure in a certain way. We indicate what kind of hiber classes we have. After that we build a factory of sessions. After that, the session factory allows us to open a session to work with exactly the classes that we specified. Next, we’ll look at how we, say, save data to the database:
This is how we can work with objects of the DataElement class. But it turns out that we need to write such methods for all classes ... Everyone has no desire to write this, especially if there are a lot of classes. Do not forget about the methods Insert, Update, Delete. In total, we have four hiber classes, for each there are at least four methods, a total of 16 methods. Laziness ...
This is where DAO + generic comes to the rescue.
So let's try to create an interface common to all hiber objects, with the word * type * we mark those types that are hiber classes, i.e. it can be any of the hiber classes we use. In my database, all PK tables have an int field.
We see that for all classes four methods are required (meaning a minimum) ... But how do we pass the type correctly? And like this:
Here we begin to use the template (generic type) of the class. Even two. The first (T) is the template for the hiber class, the second (ID) is the template for the type that is PK. Those. we will be able to take fields from the database, not only by PK with type int, but also say string.
So we created the GenericDao interface, now we need to create a class that will implement this interface. But first, let's recall the sessions.
We remember that in order to take, save, update or delete data from the database, we need to open a session. And I also note that if we took the data, transferred it to the object of the hiber class, and closed the session, then if we have Lazy disabled (disabled by default), the data in the object will not be available. What is it for? Due to the fact that the proxy class refers through the session to the data we need (what is a proxy class, read the documentation for the heber). After closing the session, it turns out that the proxy class refers to a link inaccessible to us. Those. It turns out that we should keep the session. How to do this? A session is described through the ISession interface. And we take the session from the factory sessions. How do we do this? That's how:
It turns out that the session is stored in the context when we need it, we call the GetSession () method, it checks whether the session is “alive” or not, if not, create a new one, if so, return “live”. As for the initialization, it must be called once in any place of its program, but before they started using the session. Say in the constructor of the main form. That's all. Understood ... Now let's look at the implementation of the IGenericDao interface.
We have an interface through which we implement the DAO, we have a class for working with the session, now we just have to write the CRUD methods (Create, Read, Update, Delete) to work through generic. So this happens:
And again, everything is ridiculously simple ... Now we can create GenericDao for any of our hiber classes. Now we can use the GenericImpl class to work with any of our objects.
Well, beautiful, isn't it? But we will not stop there. It seems to me that you may not like how the DAO is created. Let's try to make our life easier. How? Create a DAO factory for each hiber class.
What will we do? We will create a unique DAO for each class, but at the same time we will not write any more CRUD methods. How to do it? Simply. But in order ...
We will create a DAO interface for each hiber class, simply inheriting it from the base IGenericDao, but with an indication of which hiber class we will work with:
The downside, of course, is that for each class you need to declare your interface, but do not write CRUD methods. Next, we will make an interface for the DAO factory, which will supply us with ready-made Dao interfaces for each hiber class:
I think everything is clear what it is. And so that everything would be quite beautiful, we will create classes that will implement IGenericDAO for each individual hiber class:
It turns out that we must implement the DAO interface for each hiber class, and we implement it by inheriting the base GenericImpl indicating the hiber class. And we implement the DAO factory:
This is how we get ready-made DAO objects for working with any hiber class. How to use it? Like this:
I would like to add that if there are unresolved issues, then voice them and then maybe your question will fall into the topic of the following articles.
For those to whom this is not enough, we go HERE . Here's how to ease our problems even further using Spring FrameWork and its SpringAOP. The thing is extremely useful, but very whimsical.
That's it. I hope everyone understands. Claims, as always, are accepted and will be heard. I hope it comes in handy.)
PS: My first topic on Habr. I hope that the article will be pleasant. Hello!:-)
DAO (abbr. From English. Data Access Objects) - objects for accessing data.
Generic means that we will use the class templates (generic) available in C # to create an interface that will work with all our NHibernate classes.
1. Creating a GenericDao Interface
So let's get started. In a previous article, I talked about how to configure NHibernate. What should be the application settings file, etc.
First, let's look at how the work with NHibernate (hereinafter referred to as hibernate) occurs to access the database.
Let's analyze the class that allows us to configure NHibernate to work with our objects, we just look at this class, we will not use it anywhere:
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();//Создание фабрики сессий
}
}
* This source code was highlighted with Source Code Highlighter.
In the constructor, we see that an object of the Configuration class is created, which we need to configure in a certain way. We indicate what kind of hiber classes we have. After that we build a factory of sessions. After that, the session factory allows us to open a session to work with exactly the classes that we specified. Next, we’ll look at how we, say, save data to the database:
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();//закрываем сессию
}
}
* This source code was highlighted with Source Code Highlighter.
This is how we can work with objects of the DataElement class. But it turns out that we need to write such methods for all classes ... Everyone has no desire to write this, especially if there are a lot of classes. Do not forget about the methods Insert, Update, Delete. In total, we have four hiber classes, for each there are at least four methods, a total of 16 methods. Laziness ...
This is where DAO + generic comes to the rescue.
So let's try to create an interface common to all hiber objects, with the word * type * we mark those types that are hiber classes, i.e. it can be any of the hiber classes we use. In my database, all PK tables have an int field.
public interface IGenericDao
{
*type* Get(int id);
*type* Save(*type*obj);
*type* SaveOrUpdate(*type* obj);
void Delete(*type* obj);
}
* This source code was highlighted with Source Code Highlighter.
We see that for all classes four methods are required (meaning a minimum) ... But how do we pass the type correctly? And like this:
public interface IGenericDao
{
T Get(ID id);
T Save(T obj);
T SaveOrUpdate(T obj);
void Delete(T obj);
}
* This source code was highlighted with Source Code Highlighter.
Here we begin to use the template (generic type) of the class. Even two. The first (T) is the template for the hiber class, the second (ID) is the template for the type that is PK. Those. we will be able to take fields from the database, not only by PK with type int, but also say string.
So we created the GenericDao interface, now we need to create a class that will implement this interface. But first, let's recall the sessions.
2. Creating a session factory
We remember that in order to take, save, update or delete data from the database, we need to open a session. And I also note that if we took the data, transferred it to the object of the hiber class, and closed the session, then if we have Lazy disabled (disabled by default), the data in the object will not be available. What is it for? Due to the fact that the proxy class refers through the session to the data we need (what is a proxy class, read the documentation for the heber). After closing the session, it turns out that the proxy class refers to a link inaccessible to us. Those. It turns out that we should keep the session. How to do this? A session is described through the ISession interface. And we take the session from the factory sessions. How do we do this? That's how:
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; //Возвращаем
}
}
* This source code was highlighted with Source Code Highlighter.
It turns out that the session is stored in the context when we need it, we call the GetSession () method, it checks whether the session is “alive” or not, if not, create a new one, if so, return “live”. As for the initialization, it must be called once in any place of its program, but before they started using the session. Say in the constructor of the main form. That's all. Understood ... Now let's look at the implementation of the IGenericDao interface.
3. Implementation of IGenericDao
We have an interface through which we implement the DAO, we have a class for working with the session, now we just have to write the CRUD methods (Create, Read, Update, Delete) to work through generic. So this happens:
public class GenericImpl : IGenericDao //Реализуем интерфейс 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);
}
}
* This source code was highlighted with Source Code Highlighter.
And again, everything is ridiculously simple ... Now we can create GenericDao for any of our hiber classes. Now we can use the GenericImpl class to work with any of our objects.
GenericImpl libdao = new GenericImpl(); //Вот инструмент для работы с объектами хибер класса Library
Library lib = new Library(); //Создаем объект хибер класса
lib.Name = “Новая библиотека”; //Заполняем
libdao.Save(lib);//Так сохраняем
libdao.Get(1);//берем
libdao.Delete(lib);//Удаляем
libdao.SaveOrUpdate(lib);//Обновляем
* This source code was highlighted with Source Code Highlighter.
Well, beautiful, isn't it? But we will not stop there. It seems to me that you may not like how the DAO is created. Let's try to make our life easier. How? Create a DAO factory for each hiber class.
4. Factory DAO
What will we do? We will create a unique DAO for each class, but at the same time we will not write any more CRUD methods. How to do it? Simply. But in order ...
We will create a DAO interface for each hiber class, simply inheriting it from the base IGenericDao, but with an indication of which hiber class we will work with:
public interface IDataElementDao : IGenericDao{}
public interface ILibraryDao : IGenericDao { }
public interface IPinsDao : IGenericDao { }
public interface IPropertiesDao : IGenericDao { }
* This source code was highlighted with Source Code Highlighter.
The downside, of course, is that for each class you need to declare your interface, but do not write CRUD methods. Next, we will make an interface for the DAO factory, which will supply us with ready-made Dao interfaces for each hiber class:
public interface IDaoFactory
{
IDataElementDao GetDataElementDao();
ILibraryDao GetLibraryDao();
IPinsDao GetPinsDao();
IPropertiesDao GetPropertiesDao();
}
* This source code was highlighted with Source Code Highlighter.
I think everything is clear what it is. And so that everything would be quite beautiful, we will create classes that will implement IGenericDAO for each individual hiber class:
public class HDataElement : GenericImpl, IDataElementDao{}
public class HLibrary : GenericImpl, ILibraryDao{}
public class HPins : GenericImpl, IPinsDao{}
public class HProperties : GenericImpl, IPropertiesDao{}
* This source code was highlighted with Source Code Highlighter.
It turns out that we must implement the DAO interface for each hiber class, and we implement it by inheriting the base GenericImpl indicating the hiber class. And we implement the DAO factory:
public 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();
}
}
* This source code was highlighted with Source Code Highlighter.
This is how we get ready-made DAO objects for working with any hiber class. How to use it? Like this:
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 и можем дальше работать с ним.
* This source code was highlighted with Source Code Highlighter.
CONCLUSION
I would like to add that if there are unresolved issues, then voice them and then maybe your question will fall into the topic of the following articles.
For those to whom this is not enough, we go HERE . Here's how to ease our problems even further using Spring FrameWork and its SpringAOP. The thing is extremely useful, but very whimsical.
That's it. I hope everyone understands. Claims, as always, are accepted and will be heard. I hope it comes in handy.)
PS: My first topic on Habr. I hope that the article will be pleasant. Hello!:-)