Het DAO-patroon in Java

1. Overzicht

Het Data Access Object (DAO) -patroon is een structureel patroon waarmee we dat kunnen isoleer de applicatie- / bedrijfslaag van de persistentielaag (meestal een relationele database, maar het kan elk ander persistentiemechanisme zijn) met behulp van een abstracte API.

De functionaliteit van deze API is om alle complexiteit van het uitvoeren van CRUD-bewerkingen in het onderliggende opslagmechanisme voor de applicatie te verbergen. Hierdoor kunnen beide lagen afzonderlijk evolueren zonder iets van elkaar af te weten.

In deze tutorial gaan we dieper in op de implementatie van het patroon en leren we hoe we het kunnen gebruiken voor het abstraheren van oproepen naar een JPA-entiteitsbeheerder.

2. Een eenvoudige implementatie

Laten we een eenvoudig voorbeeld maken om te begrijpen hoe het DAO-patroon werkt.

Laten we zeggen dat we een applicatie willen ontwikkelen die gebruikers beheert. Om het domeinmodel van de applicatie volledig agnostisch te houden over de database, maken we een eenvoudige DAO-klasse die ervoor zorgt dat deze componenten netjes van elkaar worden losgekoppeld.

2.1. De domeinklasse

Omdat onze applicatie met gebruikers werkt, hoeven we slechts één klasse te definiëren voor het implementeren van het domeinmodel:

openbare klasse Gebruiker {privé Stringnaam; privé String-e-mail; // constructeurs / standaard setters / getters}

De Gebruiker class is gewoon een gewone container voor gebruikersgegevens, dus het implementeert geen ander gedrag dat de moeite waard is om te benadrukken.

De meest relevante ontwerpkeuze die we hier moeten maken, is natuurlijk hoe we de toepassing die deze klasse gebruikt, geïsoleerd houden van elk persistentiemechanisme dat op een bepaald moment zou kunnen worden geïmplementeerd.

Welnu, dat is precies het probleem dat het DAO-patroon probeert aan te pakken.

2.2. De DAO API

Laten we een basis DAO-laag definiëren, zodat we kunnen zien hoe dit kan houd het domeinmodel volledig losgekoppeld van de persistentielaag.

Hier is de DAO API:

openbare interface Dao {Optioneel get (lange id); Lijst getAll (); ongeldig opslaan (T t); ongeldige update (T t, String [] params); ongeldig verwijderen (T t); }

Vanuit vogelperspectief is het duidelijk te zien dat de Dao interface definieert een abstracte API die CRUD-bewerkingen uitvoert op objecten van het type T.

Vanwege het hoge abstractieniveau dat de interface biedt, is het gemakkelijk om een ​​concrete, fijnmazige implementatie te maken die werkt met Gebruiker voorwerpen.

2.3. De Gebruiker Klasse

Laten we een gebruikersspecifieke implementatie van het Dao koppel:

public class Userensional implementeert Dao {private List-gebruikers = nieuwe ArrayList (); public Userensional () {users.add (nieuwe gebruiker ("John", "[email protected]")); users.add (nieuwe gebruiker ("Susan", "[email protected]")); } @Override public Optioneel get (lange id) {retourneer Optioneel.ofNullable (gebruikers.get ((int) id)); } @Override openbare lijst getAll () {terugkerende gebruikers; } @Override public void save (User user) {users.add (user); } @Override public void update (User user, String [] params) {user.setName (Objects.requireNonNull (params [0], "Name mag niet null zijn")); user.setEmail (Objects.requireNonNull (params [1], "E-mail mag niet null zijn")); users.add (gebruiker); } @Override public void delete (User user) {users.remove (user); }}

De Gebruiker class implementeert alle functionaliteit die nodig is voor het ophalen, bijwerken en verwijderen Gebruiker voorwerpen.

Eenvoudigheidshalve is de gebruikerslijst werkt als een in-memory database, die gevuld is met een paar Gebruiker objecten in de constructor.

Het is natuurlijk gemakkelijk om de andere methoden te refactoren, zodat ze bijvoorbeeld kunnen werken met een relationele database.

Terwijl zowel de Gebruiker en Gebruiker klassen onafhankelijk naast elkaar bestaan ​​binnen dezelfde applicatie, we moeten nog steeds zien hoe de laatste kan worden gebruikt om de persistentielaag verborgen te houden voor applicatielogica:

openbare klasse UserApplication {privé statische Dao userão; openbare statische leegte hoofd (String [] args) {userão = nieuwe Userão (); Gebruiker user1 = getUser (0); System.out.println (gebruiker1); userensional.update (user1, new String [] {"Jake", "[email protected]"}); Gebruiker user2 = getUser (1); userbao.delete (gebruiker2); userensional.save (nieuwe gebruiker ("Julie", "[email protected]")); userbao.getAll (). forEach (gebruiker -> System.out.println (user.getName ())); } privé statische gebruiker getUser (lange id) {Facultatieve gebruiker = userbao.get (id); return user.orElseGet (() -> nieuwe gebruiker ("niet-bestaande gebruiker", "geen e-mail")); }}

Het voorbeeld is bedacht, maar het toont in een notendop de motivaties achter het DAO-patroon. In dit geval is het hoofd methode gebruikt alleen een Gebruiker instantie om CRUD-bewerkingen uit te voeren op een paar Gebruiker voorwerpen.

Het meest relevante aspect van dit proces is hoe Gebruiker verbergt voor de toepassing alle details op laag niveau over hoe de objecten worden bewaard, bijgewerkt en verwijderd.

3. Het patroon gebruiken met JPA

Er is een algemene tendens onder ontwikkelaars om te denken dat de release van JPA de functionaliteit van het DAO-patroon heeft gedowngraded naar nul, aangezien het patroon gewoon een nieuwe laag van abstractie en complexiteit wordt die wordt geïmplementeerd bovenop die van de entiteitsbeheerder van JPA.

In sommige scenario's is dit ongetwijfeld waar. Toch, soms willen we slechts een paar domeinspecifieke methoden van de API van de entiteitsbeheerder aan onze applicatie blootstellen. In dergelijke gevallen heeft het DAO-patroon zijn plaats.

3.1. De JpaUserbao Klasse

Met dat gezegd, laten we een nieuwe implementatie van het Dao interface, zodat we kunnen zien hoe het de functionaliteit kan inkapselen die de entiteitsbeheerder van JPA out-of-the-box biedt:

openbare klasse JpaUserbao implementeert Dao {private EntityManager entityManager; // standard constructors @Override public Optioneel get (lange id) {return Optional.ofNullable (entityManager.find (User.class, id)); } @Override openbare lijst getAll () {Queryquery = entityManager.createQuery ("SELECTEER e VAN Gebruiker e"); return query.getResultList (); } @Override public void save (gebruiker gebruiker) {executeInsideTransaction (entityManager -> entityManager.persist (gebruiker)); } @Override public void update (User user, String [] params) {user.setName (Objects.requireNonNull (params [0], "Name mag niet null zijn")); user.setEmail (Objects.requireNonNull (params [1], "E-mail mag niet null zijn")); executeInsideTransaction (entityManager -> entityManager.merge (gebruiker)); } @Override public void delete (gebruiker gebruiker) {executeInsideTransaction (entityManager -> entityManager.remove (gebruiker)); } private void executeInsideTransaction (actie van de consument) {EntityTransaction tx = entityManager.getTransaction (); probeer {tx.begin (); action.accept (entityManager); tx.commit (); } catch (RuntimeException e) {tx.rollback (); gooi e; }}}

De JpaUserbao class kan werken met elke relationele database die wordt ondersteund door de JPA-implementatie.

Bovendien, als we goed naar de klas kijken, zullen we ons realiseren hoe het gebruik van Composition and Dependency Injection ons in staat stelt alleen de entiteitsbeheermethoden aan te roepen die door onze applicatie vereist zijn.

Simpel gezegd, we hebben een domeinspecifieke API op maat, in plaats van de volledige API van de entiteitsbeheerder.

3.2. Refactoring van het Gebruiker Klasse

In dit geval gebruiken we Hibernate als de standaard JPA-implementatie, dus we zullen het Gebruiker klasse dienovereenkomstig:

@Entity @Table (naam = "gebruikers") openbare klasse Gebruiker {@Id @GeneratedValue (strategie = GenerationType.AUTO) lange privé-id; private String naam; privé String-e-mail; // standard constructors / setters / getters}

3.3. Bootstrapping van een JPA Entity Manager Programmatisch

Ervan uitgaande dat we al een werkend exemplaar van MySQL hebben dat lokaal of op afstand wordt uitgevoerd en een databasetabel "Gebruikers" gevuld met enkele gebruikersrecords, hebben we een JPA-entiteitsbeheerder nodig, zodat we de JpaUserbao klasse voor het uitvoeren van CRUD-bewerkingen in de database.

In de meeste gevallen bereiken we dit via de typische "Persistence.xml" bestand, wat de standaardaanpak is.

In dit geval nemen we een "Xml-less" benader en verkrijg de entiteitsbeheerder met gewoon Java via Hibernate's handig EntityManagerFactoryBuilderImpl klasse.

Raadpleeg dit artikel voor een gedetailleerde uitleg over het opstarten van een JPA-implementatie met Java.

3.4. De UserApplication Klasse

Laten we tot slot de initiaal refactoren UserApplication klasse, zodat het kan werken met een JpaUserbao instantie en voer CRUD-bewerkingen uit op het Gebruiker entiteiten:

openbare klasse UserApplication {privé statisch Dao jpaUserbao; // standard constructors public static void main (String [] args) {User user1 = getUser (1); System.out.println (gebruiker1); updateUser (user1, new String [] {"Jake", "[email protected]"}); saveUser (nieuwe gebruiker ("Monica", "[email protected]")); deleteUser (getUser (2)); getAllUsers (). forEach (gebruiker -> System.out.println (user.getName ())); } openbare statische gebruiker getUser (lange id) {Facultatieve gebruiker = jpaUserbao.get (id); return user.orElseGet (() -> nieuwe gebruiker ("niet-bestaande gebruiker", "geen e-mail")); } openbare statische lijst getAllUsers () {return jpaUserbao.getAll (); } openbare statische ongeldige updateUser (gebruiker gebruiker, string [] params) {jpaUserbao.update (gebruiker, params); } openbare statische leegte saveUser (gebruiker gebruiker) {jpaUserbao.save (gebruiker); } openbare statische ongeldige deleteUser (gebruiker gebruiker) {jpaUserbao.delete (gebruiker); }}

Zelfs als het voorbeeld inderdaad vrij beperkt is, blijft het nuttig om te demonstreren hoe de functionaliteit van het DAO-patroon kan worden geïntegreerd met die van de entiteitsbeheerder.

In de meeste toepassingen is er een DI-framework, dat verantwoordelijk is voor het injecteren van een JpaUserbao instantie in het UserApplication klasse. Voor de eenvoud hebben we de details van dit proces weggelaten.

Het meest relevante punt om hier te benadrukken is hoe de JpaUserbao class helpt om het UserApplication klasse volledig agnostisch over hoe de persistentielaag CRUD-bewerkingen uitvoert.

Bovendien zouden we MySQL kunnen ruilen voor elk ander RDBMS (en zelfs voor een platte database) verderop, en toch zou onze applicatie blijven werken zoals verwacht, dankzij het abstractieniveau dat wordt geboden door de Dao interface en de entiteitsbeheerder.

4. Conclusie

In dit artikel hebben we dieper ingegaan op de belangrijkste concepten van het DAO-patroon, hoe het in Java kan worden geïmplementeerd en hoe het te gebruiken bovenop de entiteitsbeheerder van JPA.

Zoals gewoonlijk zijn alle codevoorbeelden die in dit artikel worden getoond, beschikbaar op GitHub.


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