Introduksjon til DAO

Når du jobber med en database gjennom JDBC eller til og med gjennom Hibernate, viser koden seg ofte å være mer tungvint enn vi ønsker. En databasespørring inneholder ofte:

  • datavalidering
  • stille inn forespørselsparametere
  • HQL-spørringsvalg avhengig av spørringsparametere
  • konstruere en spørring ved hjelp av Criteria API
  • caching innstillinger
  • innledende feilhåndtering osv.

Derfor er vanlig praksis å lage spesialklasser for arbeid med databasen. Slike klasser kalles DAO, Data Access Object. Deres oppgave er å skjule all kompleksiteten ved å jobbe med databasen og gi et vakkert og praktisk grensesnitt til utsiden.

Eksempel:

public class EmployeeDAO {

   public List<Employee> getEmployeeList(int from, int count) {
   	String hqlQuery = “from Employee;
   	Query<Employee> query = session.createQuery(hqlQuery, Employee.class);
   	query.setFirstResult(from);
   	query.setMaxResults(count);
   	return query.getResultList();
  }

	public int getEmployeeCount() {
	     String hqlQuery = “select count(*) from Employee;
     	Query<Integer> query = session.createQuery(hqlQuery, Integer.class);
     	return query.getSingleResult();
   }

	public Employee getEmployeeByUniqName(String name) {
	     String hqlQuery = “from Employee where name = :name”;
     	Query<Integer> query = session.createQuery(hqlQuery, Employee.class);
     	query.setParameterr(“name”, name);
     	return query.getSingleResult();
   }
}

Vi har en klasse EmployeeDAO , ved hjelp av denne henter vi objekter av typen Employee fra databasen. Selv om klassen er fylt med merknader, inneholder den ikke metoder for å lagre seg selv i databasen.

Fordeler med DAO

Det er mange fordeler med denne tilnærmingen:

For det første har vi fullstendig skjult arbeidet med databasen i DAO-klassen. Hvis du i fremtiden bestemmer deg for å omskrive alle spørringer fra HQL til Criteria API eller Native Query, vil dette ikke påvirke koden utenfor denne klassen på noen måte.

For det andre kan du komplisere oppførselen til disse metodene. Du kan legge til caching, hendelser, parametervalidering. Alt dette vil være skjult fra utsiden koden.

For det tredje, hvis du trenger en metode som ikke eksisterer ennå, legger du den til her. For eksempel trenger jeg en metode som vil returnere alle brukeroppgaver som allerede er utløpt. Da gjør jeg bare dette:

public class EmployeeDAO {

   public List<Task> getExpiredTasks(int userId, int from, int count) {
   	String hqlQuery = “from Task where task.user.id = :id and deadline < curdate();
   	Query<Task> query = session.createQuery(hqlQuery, Task.class);
   	query.setFirstResult(from);
   	query.setMaxResults(count);
   	return query.getResultList();
  }

   public int getExpiredTasksCount(int userId) {
   	String hqlQuery = “select count(*) from Task where task.user.id = :id and deadline < curdate();
   	Query<Integer> query = session.createQuery(hqlQuery, Integer.class);
   	return query.getSingleResult();
  }
}

Jeg la til to metoder til klassen:

  • getExpiredTasksCount() - returnerer antall utløpte oppgaver for brukeren
  • getExpiredTasks() - returnerer en liste over utløpte oppgaver for brukeren

Jeg trenger metoder - jeg la dem til. Og jeg kan bruke den med en gang. Jeg skal optimere dem senere.

Dessuten kan disse metodene dekkes med enhetstester før omskriving og optimaliseringer, slik at vi vet at arbeidet med databasen har vært det samme som det var.

Standard tilnærming

Svært ofte har DAO-klasser metoder som er de samme. For eksempel disse:

T getById (endelig lang id) Få et objekt etter dets id
List<T> getItems (int fra, int count) Få en liste over objekter innenfor et gitt område
List<T> getAll () Få alle objekter av en gitt type
int getCount () Finn ut antall gjenstander
T lagre (endelig T-enhet) Lagre objekt i databasen
T -oppdatering (endelig T-enhet) Oppdater objekt i database
ugyldig sletting (endelig T-enhet) Slett et objekt fra databasen
void deleteById (endelig lang enhets-ID) Slett objekt fra databasen etter id

Disse metodene finnes i nesten alle DAO-klasser i verden. Nå, hvis det er en slags DAO-klasse, vil den med 90% sannsynlighet ha slike metoder.

Ja, det kan være andre, men det vil være de også. Fordi det er veldig praktisk. Og det lar deg ikke samhandle med basen eller Hibernate direkte.

Og hvis det er identiske metoder, hva trenger de da? Det stemmer, legg det i grunnklassen.

Det ser omtrent slik ut:

public abstract class AbstractHibernateDao<T > {
    private final Class<T> clazz;
    private SessionFactory sessionFactory;

    public AbstractHibernateDao(final Class<T> clazzToSet)   {
    	this.clazz = clazzToSet;
    }

    public T getById(final long id) {
    	return (T) getCurrentSession().get(clazz, id);
    }

    public List<T> getItems(int from, int count) {
    	Query query = getCurrentSession().createQuery(clazz , "from " + clazz.getName())
    	query.setFirstResult(offset);
    	query.setMaxResults(count);
  	  return query.singleResult();
    }

    public List<T> findAll() {
    	return getCurrentSession().createQuery(clazz, "from " + clazz.getName()).list();
    }

    public T create(final T entity) {
    	getCurrentSession().saveOrUpdate(entity);
    	return entity;
    }

    public T update(final T entity) {
    	return (T) getCurrentSession().merge(entity);
    }

    public void delete(final T entity) {
    	getCurrentSession().delete(entity);
    }

    public void deleteById(final long entityId) {
    	final T entity = getById(entityId);
    	delete(entity);
    }

    protected Session getCurrentSession() {
    	return sessionFactory.getCurrentSession();
    }
}

Og da vil vår EmployeeDAO se slik ut:

public class EmployeeDAO extends AbstractHibernateDAO<Employee> {

   public EmployeeDAO (){
  	super(Employee.class );
   }
}

Og TaskDAO er slik:

public class TaskDAO extends AbstractHibernateDAO<Task> {

   public TaskDAO (){
  	super(Task.class );
   }
}

Og begge disse klassene vil ha alle metodene vi erklærte på AbstractHibernateDAO . Forening er veldig praktisk og praktisk.