⬆️ ⬇️

Nhibernate. DAO + Generic

Hello. What is GenericDao, you should know, but if you do not know:

DAO (abbr. From the English. Data Access Objects) - objects for data access.

Generic means that we will use the generic class templates (available in C #) to create such an interface that allows working with all our NHibernate classes.



1. Creating the GenericDao interface



So let's get started. In the last article I talked about how to configure NHibernate. What should be the application settings file, etc.

To begin with, we will look at how work with NHibernate (hereinafter referred to as Hiber, Hibernate), for access to the database, takes place.

Let us analyze the class that allows us to configure NHibernate to work with our objects, we just consider 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 the object of the Configuration class is being created, which we should configure in a certain way. We specify which heber 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 those classes that we specified. Next, consider how we, say, save the data in 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 it, 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 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 heber objects, with the word * type * we mark those types that are heber classes, i.e. This can be any of the 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 the minimum) ... But how can we transfer the type correctly? That's how:

public interface IGenericDao<T, ID>

{

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) class. Even two. The first (T) is the template for the heber class, the second (ID) is the template for the type of which is PK. Those. we can take fields from the database, not only for PK with the int type, but also say string.

So we created the GenericDao interface, now we need to create a class that will implement this interface. But first, remember 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 the session. And I also note that if we took the data, transferred it to the heber class object, 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 the proxy class, read the documentation for the heber). After the session is closed, it turns out that the proxy class refers to the inaccessible link. Those. It turns out that we must keep the session. How to do it? Session is described through the ISession interface. And we take the session from the session factory. 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 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 yes, return “live”. As for the initialization, it should be called once at any place of its program, but before it began to use the session. Let's say in the constructor of the main form. That's all. Understood ... Now let's look at the implementation of the IGenericDao interface.

3. Implementing IGenericDao



We have an interface through which we implement DAO, we have a class for working with the session, now we have to write CRUD (Create, Read, Update, Delete) methods for working through generic. So it happens:

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

}

}




* This source code was highlighted with Source Code Highlighter .


And again everything is ridiculously simple ... Now we can create GenericDao for any of our heber class. Now we can use the GenericImpl class to work with any of our objects.

GenericImpl<Library, int > libdao = new GenericImpl<Library, int >(); // 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, is not it? But we will not stop at this. It seems to me that you may not like the way DAO is created. Let's try to make your life easier. How? Create a DAO factory for each heber class.

4. Factory DAO



What do we do? We will create our own DAO for each class, unique, but we will not write a single CRUD method anymore. How to do it? Simply. But in order ...

We will create a DAO interface for each heber class, simply inheriting it from the basic IGenericDao, but with an indication of which heber class we will work with:

public interface IDataElementDao : IGenericDao<DataElement, int >{}

public interface ILibraryDao : IGenericDao<Library, int > { }

public interface IPinsDao : IGenericDao<Pins, int > { }

public interface IPropertiesDao : IGenericDao<Properties, int > { }




* This source code was highlighted with Source Code Highlighter .


The minus of course is that for each class you need to declare your interface, but you do not need to write CRUD methods. Next, we will create an interface for the DAO factory, which will supply us with ready-made Dao interfaces for each heber 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 heber class:

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




* This source code was highlighted with Source Code Highlighter .


It turns out that we have to implement the DAO interface for each heber class, and we implement it by inheriting the base GenericImpl with an indication of the heber class. And we realize 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 DAO objects for working with any heber 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 unsolved issues, then voice them and then maybe your question will fall into the subject of the following articles.

For those to whom this is not enough, then we go HERE . It describes how to alleviate our problems even more with the use of Spring FrameWork and its SpringAOP. The thing is extremely useful, but very whimsical.

Well that's all. I hope everyone understands everything. Claims, as always, all are accepted and will be heard. I hope it will come in handy.)



PS: My first topic on Habré. I hope that the article will like it. Hello!:-)


')

Source: https://habr.com/ru/post/50187/



All Articles