DAO-mønsteret i Java

1. Oversikt

Data Access Object (DAO) mønsteret er et strukturelt mønster som lar oss isoler applikasjons- / forretningslaget fra utholdenhetslaget (vanligvis en relasjonsdatabase, men det kan være en hvilken som helst annen utholdenhetsmekanisme) ved hjelp av et abstrakt API.

Funksjonaliteten til denne API-en er å skjule fra komplekset alle kompleksitetene som er involvert i å utføre CRUD-operasjoner i den underliggende lagringsmekanismen. Dette gjør at begge lagene kan utvikle seg hver for seg uten å vite noe om hverandre.

I denne veiledningen tar vi et dypdykk i implementeringen av mønsteret, og vi lærer hvordan vi bruker det til å abstrahere samtaler til en JPA-enhetsleder.

2. En enkel implementering

For å forstå hvordan DAO-mønsteret fungerer, la oss lage et grunnleggende eksempel.

La oss si at vi ønsker å utvikle et program som administrerer brukere. For å holde applikasjonens domenemodell fullstendig agnostisk om databasen, oppretter vi en enkel DAO-klasse som tar seg av å holde disse komponentene pent frakoblet fra hverandre.

2.1. Domeneklassen

Siden applikasjonen vår vil fungere med brukere, må vi definere bare en klasse for å implementere domenemodellen:

offentlig klasse bruker {privat strengnavn; privat streng e-post; // konstruktører / standard settere / getters}

De Bruker klasse er bare en vanlig beholder for brukerdata, så den implementerer ingen annen oppførsel som er verdt å understreke.

Selvfølgelig er det mest relevante designvalget vi trenger å gjøre her, hvordan vi holder applikasjonen som bruker denne klassen isolert fra enhver utholdenhetsmekanisme som kan implementeres på et tidspunkt.

Vel, det er akkurat problemet som DAO-mønsteret prøver å løse.

2.2. DAO API

La oss definere et grunnleggende DAO-lag, slik at vi kan se hvordan det kan holde domenemodellen helt frakoblet fra utholdenhetslaget.

Her er DAO API:

offentlig grensesnitt Dao {Valgfritt get (lang id); Liste getAll (); ugyldig lagre (T t); ugyldig oppdatering (T t, String [] params); ugyldig sletting (T t); }

Fra et fugleperspektiv er det klart å se at Dao grensesnitt definerer et abstrakt API som utfører CRUD-operasjoner på objekter av typen T.

På grunn av det høye nivået av abstraksjon som grensesnittet gir, er det enkelt å lage en konkret, finkornet implementering som fungerer med Bruker gjenstander.

2.3. De UserDao Klasse

La oss definere en brukerspesifikk implementering av Dao grensesnitt:

offentlig klasse UserDao implementerer Dao {private List brukere = ny ArrayList (); offentlig UserDao () {brukere.add (ny bruker ("John", "[e-postbeskyttet]")); users.add (ny bruker ("Susan", "[email protected]")); } @ Override public Valgfritt get (long id) {return Optional.ofNullable (users.get ((int) id)); } @Override public List getAll () {returnere brukere; } @ Overstyr offentlig ugyldig lagring (brukerbruker) {brukere.add (bruker); } @Override offentlig ugyldig oppdatering (brukerbruker, streng [] params) {user.setName (Objects.requireNonNull (params [0], "Navnet kan ikke være null")); user.setEmail (Objects.requireNonNull (params [1], "E-post kan ikke være null")); users.add (bruker); } @ Overstyr offentlig ugyldig sletting (brukerbruker) {brukere. Fjern (bruker); }}

De UserDao class implementerer all funksjonaliteten som kreves for å hente, oppdatere og fjerne Bruker gjenstander.

For enkelhets skyld er den brukerliste fungerer som en database i minnet, som er fylt med et par Bruker gjenstander i konstruktøren.

Selvfølgelig er det enkelt å omformulere de andre metodene, slik at de for eksempel kan jobbe med en relasjonsdatabase.

Mens begge de Bruker og UserDao klasser eksisterer uavhengig i samme applikasjon, må vi fortsatt se hvordan sistnevnte kan brukes til å holde utholdenhetslaget skjult fra applikasjonslogikken:

offentlig klasse UserApplication {privat statisk Dao userDao; public static void main (String [] args) {userDao = new UserDao (); Brukerbruker1 = getUser (0); System.out.println (bruker1); userDao.update (user1, new String [] {"Jake", "[email protected]"}); Brukerbruker2 = getUser (1); userDao.delete (user2); userDao.save (ny bruker ("Julie", "[e-postbeskyttet]")); userDao.getAll (). forEach (user -> System.out.println (user.getName ())); } privat statisk bruker getUser (lang id) {Valgfri bruker = userDao.get (id); returner user.orElseGet (() -> ny bruker ("ikke-eksisterende bruker", "ingen e-post")); }}

Eksemplet er konstruert, men det viser, i et nøtteskall, motivasjonene bak DAO-mønsteret. I dette tilfellet hoved- metoden bruker bare en UserDao eksempel for å utføre CRUD-operasjoner på noen få Bruker gjenstander.

Den mest relevante fasetten i denne prosessen er hvordan UserDao skjuler fra applikasjonen alle detaljer på lavt nivå om hvordan objektene vedvares, oppdateres og slettes.

3. Bruke mønsteret med JPA

Det er en generell tendens blant utviklere å tro at utgivelsen av JPA nedgradert til null DAO-mønsterets funksjonalitet, ettersom mønsteret bare blir enda et lag med abstraksjon og kompleksitet implementert på toppen av det som tilbys av JPAs enhetsleder.

Utvilsomt, i noen scenarier er dette sant. Likevel, noen ganger vil vi bare utsette for applikasjonen vår bare noen få domenespesifikke metoder for enhetsadministratorens API. I slike tilfeller har DAO-mønsteret sin plass.

3.1. De JpaUserDao Klasse

Med det sagt, la oss lage en ny implementering av Dao grensesnitt, slik at vi kan se hvordan det kan kapsle funksjonaliteten som JPAs enhetsbehandling gir ut av boksen:

offentlig klasse JpaUserDao implementerer Dao {private EntityManager entityManager; // standardkonstruktører @ Override public Valgfri get (lang id) {retur Optional.ofNullable (entityManager.find (User.class, id)); } @Override public List getAll () {Query query = entityManager.createQuery ("SELECT e FROM User e"); returner spørring.getResultList (); } @ Overstyr offentlig tomrom lagre (brukerbruker) {executeInsideTransaction (entityManager -> entityManager.persist (bruker)); } @Override offentlig ugyldig oppdatering (brukerbruker, streng [] params) {user.setName (Objects.requireNonNull (params [0], "Navnet kan ikke være null")); user.setEmail (Objects.requireNonNull (params [1], "E-post kan ikke være null")); executeInsideTransaction (entityManager -> entityManager.merge (bruker)); } @ Overstyr offentlig tomrom slett (brukerbruker) {executeInsideTransaction (entityManager -> entityManager.remove (bruker)); } privat tomrom executeInsideTransaction (Forbrukerhandling) {EntityTransaction tx = entityManager.getTransaction (); prøv {tx.begin (); action.accept (entityManager); tx.commit (); } fange (RuntimeException e) {tx.rollback (); kaste e; }}}

De JpaUserDao klasse er i stand til å jobbe med hvilken som helst relasjonsdatabase som støttes av JPA-implementeringen.

Videre, hvis vi ser nøye på klassen, vil vi innse hvordan bruken av komposisjon og avhengighetsinjeksjon lar oss kun ringe enhetsadministrasjonsmetodene som kreves av applikasjonen vår.

Enkelt sagt, vi har et domenespesifikt skreddersydd API, i stedet for hele enhetslederens API.

3.2. Refactoring av Bruker Klasse

I dette tilfellet vil vi bruke dvalemodus som JPA-standardimplementering, og dermed vil vi omformulere Bruker klasse deretter:

@Entity @Table (name = "brukere") offentlig klasse bruker {@Id @GeneratedValue (strategi = GenerationType.AUTO) privat lang id; privat strengnavn; privat streng e-post; // standard konstruktører / settere / getters}

3.3. Bootstrapping en JPA Entity Manager Programmatisk

Forutsatt at vi allerede har en fungerende forekomst av MySQL som kjører enten lokalt eller eksternt, og en databasetabell “Brukere” fylt med noen brukeroppføringer, må vi skaffe en JPA-enhetsleder, slik at vi kan bruke JpaUserDao klasse for å utføre CRUD-operasjoner i databasen.

I de fleste tilfeller oppnår vi dette via det typiske “Persistence.xml” fil, som er standard tilnærming.

I dette tilfellet tar vi en “Xml-less” nærme deg og få enhetslederen med vanlig Java gjennom dvalemodus EntityManagerFactoryBuilderImpl klasse.

For en detaljert forklaring på hvordan du kan starte en JPA-implementering med Java, vennligst sjekk denne artikkelen.

3.4. De Brukerapplikasjon Klasse

Til slutt, la oss omforme initialen Brukerapplikasjon klasse, slik at den kan fungere med en JpaUserDao forekomme og utføre CRUD-operasjoner på Bruker enheter:

offentlig klasse UserApplication {privat statisk Dao jpaUserDao; // standardkonstruktører offentlig statisk ugyldig hoved (String [] args) {Bruker bruker1 = getUser (1); System.out.println (bruker1); updateUser (user1, new String [] {"Jake", "[email protected]"}); saveUser (ny bruker ("Monica", "[email protected]")); deleteUser (getUser (2)); getAllUsers (). forEach (bruker -> System.out.println (user.getName ())); } offentlig statisk bruker getUser (lang id) {Valgfri bruker = jpaUserDao.get (id); returner user.orElseGet (() -> ny bruker ("ikke-eksisterende bruker", "ingen e-post")); } offentlig statisk liste getAllUsers () {return jpaUserDao.getAll (); } public static void updateUser (User user, String [] params) {jpaUserDao.update (user, params); } offentlig statisk ugyldig saveUser (brukerbruker) {jpaUserDao.save (bruker); } offentlig statisk ugyldig deleteUser (brukerbruker) {jpaUserDao.delete (bruker); }}

Selv når eksemplet er ganske begrenset, forblir det nyttig for å demonstrere hvordan du integrerer DAO-mønsterets funksjonalitet med den som enhetslederen gir.

I de fleste applikasjoner er det et DI-rammeverk som er ansvarlig for å injisere en JpaUserDao eksempel inn i Brukerapplikasjon klasse. For enkelhets skyld har vi utelatt detaljene i denne prosessen.

Det mest relevante punktet å stresse her er hvordan de JpaUserDao klassen hjelper til med å beholde Brukerapplikasjon klasse helt agnostisk om hvordan utholdenhetslaget utfører CRUD-operasjoner.

I tillegg kan vi bytte MySQL for andre RDBMS (og til og med for en flat database) lenger nede, og fortsatt vil søknaden fortsette å fungere som forventet, takket være abstraksjonsnivået fra Dao grensesnitt og enhetsleder.

4. Konklusjon

I denne artikkelen tok vi en grundig titt på DAO-mønsterets nøkkelkonsepter, hvordan du implementerer det i Java, og hvordan du bruker det på toppen av JPAs enhetsleder.

Som vanlig er alle kodeeksemplene vist i denne artikkelen tilgjengelig på GitHub.


$config[zx-auto] not found$config[zx-overlay] not found