Zephyrnet-Logo

Datenübertragungsobjektmuster in Java - Implementierung und Zuordnung

Datum:


Einleitung

Eine Unternehmensanwendung ist eine Softwarelösung, die für die Anforderungen eines Unternehmens erstellt wurde. Es ist oft ein großes, mehrstufiges, skalierbares System. Unternehmenssoftware kann mit vielen komplexen Daten umgehen, und es ist wichtig, dass diese Art von Software eine gute Architektur aufweist.

Architekturmuster für Unternehmensanwendungen sind standardisierte Lösungen für häufig auftretende Probleme in großen Systemen. Sie fördern das architektonische Denken und helfen Entwicklern, Systeme mit nachweislicher Zuverlässigkeit sicherer zu bauen.

Unternehmensanwendungen können mit der Bearbeitung, Anzeige oder Speicherung großer Datenmengen beauftragt werden. Das Vermeiden einer engen Kopplung und das Sicherstellen der Datenintegrität / -sicherheit darf bei der Arbeit an diesen Anwendungen kein nachträglicher Gedanke sein.

Datenübertragungsobjekt

Das Entwurfsmuster für Datenübertragungsobjekte ist eines der Architekturmuster für Unternehmensanwendungen, das die Verwendung von Objekten erfordert, die Daten für die Übertragung aggregieren und kapseln. EIN Datenübertragungsobjekt ist im Wesentlichen wie eine Datenstruktur. Es sollte keine Geschäftslogik enthalten, sondern Serialisierungs- und Deserialisierungsmechanismen enthalten.

DTOs können entweder alle Daten aus einer Quelle oder Teildaten enthalten. Sie können auch Daten aus einer oder mehreren Quellen enthalten. Bei der Implementierung werden DTOs zum Mittel für den Datentransport zwischen Systemen.

Martin Fowler beschreibt die Datenübertragungsobjekt in seinem berühmten Buch Muster der Unternehmensanwendungsarchitektur. Dort ist die Hauptidee von DTOs ist die Anzahl der teuren Ferngespräche zu reduzieren.

Martin Fowler definiert auch eine Assembler-Objekt, wird verwendet, um Daten zwischen dem DTO und beliebigen Entitätsobjekten zu konvertieren. Heutzutage verwenden wir Mapper zu diesem Zweck.

Bemerkenswert ist, dass das Anwenden des Datenübertragungsobjektmusters in lokalen Systemen zu einem Anti-Muster werden kann. Es soll in Fernanrufen verwendet werden, um die Sicherheit und die lose Kopplung zu fördern. Bei Anwendung auf lokale Systeme wird lediglich eine einfache Funktion überarbeitet.

Motivation

Nehmen wir an, wir müssen ein Unternehmenssystem für ein Unternehmen entwickeln. Das System enthält eine Datenbank mit verschiedenen allgemeinen Informationen zu Mitarbeitern - Gehalt, Projekte, Zertifikate, personenbezogene Daten (Adresse, Familienstand, Telefonnummer usw.).

Die Sicherheit am Eingang des Unternehmens erfordert den Zugriff auf unser System, um den Mitarbeiter zu identifizieren, der eintreten möchte. Sie benötigen einige rudimentäre Informationen wie den Nachnamen und das Foto des Arbeitnehmers.

We nicht Sie möchten andere vertrauliche Informationen an das Sicherheitssystem senden, z. B. persönliche Informationen. Es ist redundant und setzt den Kommunikationskanal zwischen den Systemen Angriffen aus. Wir stellen nur das zur Verfügung, was benötigt wird, und der Datenumfang wird in einem DTO definiert.

In Java-Anwendungen verwenden wir Entitätsklassen, um Tabellen in einer relationalen Datenbank darzustellen. Ohne DTOs müssten wir die gesamten Entitäten einer Remote-Schnittstelle aussetzen. Dies führt zu einer starken Kopplung zwischen einer API und einem Persistenzmodell.

Durch die Verwendung eines DTO, um nur die erforderlichen Informationen zu übertragen, lösen wir die Kopplung zwischen der API und unserem Modell, sodass wir den Service einfacher warten und skalieren können.

Implementieren eines Datenübertragungsobjekts

Lassen Sie uns eine Anwendung erstellen, die sich um die Standortverfolgung für Ihre Freunde kümmert. Wir werden eine Spring Boot-Anwendung erstellen, die eine REST-API verfügbar macht. Mithilfe dieser Funktion können wir Benutzerstandorte aus einer H2-Datenbank abrufen.

Wenn Sie weiterlesen möchten Integration einer H2-Datenbank in Spring Boot, Wir geben dir Deckung!

Spring Boot einrichten

Der einfachste Weg, um mit einer leeren Spring Boot-App zu beginnen, ist die Verwendung Frühlings-Initialisierung:

Spring Boot Initialisierung

Alternativ können Sie auch die verwenden Spring Boot-CLI So booten Sie die Anwendung:

$ spring init --dependencies=h2 data-transfer-object-demo

Wenn Sie bereits eine Maven / Spring-Anwendung haben, fügen Sie die Abhängigkeit zu Ihrer hinzu pom.xml Datei:

<dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <version>${version}</version>
</dependency>

Oder wenn Sie Gradle verwenden:

compile group: 'com.h2database', name: 'h2', version: '${version}'

Demo-Anwendung

Beginnen wir mit dem User Modell:

@Entity
public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String username; private String firstName; private String lastName; private String password; private String email; @ManyToOne(fetch = FetchType.EAGER, optional = false) @JoinColumn(name = "location_id") private Location location; // Getters and Setters
}

Es enthält einige rudimentäre Informationen wie die username, firstName, emailusw. Es hat auch eine Eins-zu-Eins-Beziehung mit dem Location Entität:

@Entity
public class Location { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private double lat; private double lng; private String place; private String description; // Getters and Setters
}

Für Grund GRAUSAM Operationen verlassen wir uns auf die vertrauenswürdigen CrudRepository bereitgestellt von Spring Boot:

@Repository
public interface UserRepository extends CrudRepository<User, Long>{}

@Repository
public interface LocationRepository extends CrudRepository<Location, Long> {}

Wenn Sie sich nicht sicher sind, wie diese funktionieren, empfehlen wir Ihnen, unsere zu lesen Leitfaden zu Spring Data JPA. Kurz gesagt, sie werden uns mit grundlegenden CRUD-Funktionen für unsere Modelle booten.

An dieser Stelle möchten wir einen Controller erstellen, der a handhabt BESTELLE Anforderung und gibt eine Liste der Standorte des Benutzers zurück. Aber wenn wir abrufen User und Location Objekte aus unserer Datenbank und drucken Sie einfach die erforderlichen Informationen aus - die anderen Informationen, wie z. B. das Kennwort, sind ebenfalls in diesem Objekt enthalten. Wir werden es nicht drucken, aber es wird da sein.

Lassen Sie uns eine machen Datenübertragungsobjekt nur die erforderlichen Informationen zu übertragen. Und wenn wir schon dabei sind, lassen Sie uns das zusammenfassen User und Location Informationen, damit die Daten zusammen übertragen werden:

public class UserLocationDTO { private Long userId; private String username; private double lat; private double lng; private String place; // Getters and Setters
} 

Dieses Objekt enthält jetzt alle Informationen, die wir dem Endbenutzer anzeigen möchten. Jetzt brauchen wir einen Weg zu Karte User und Location Objekte zu einem einzigen UserLocationDTO Objekt. Dies erfolgt normalerweise über Mapping-Tools wie z Kartenstruktur or ModelMapper, die wir in den letzten Abschnitten untersuchen werden.

Lassen Sie uns die Konvertierung zunächst manuell durchführen. Da brauchen wir einen Service, der unsere anruft UserRepositoryDort werden auch die Ergebnisse zugeordnet und die DTOs zurückgegeben:

@Service
public class MapService { @Autowired private UserRepository userRepository; public List<UserLocationDTO> getAllUsersLocation() { return ((List<User>) userRepository .findAll()) .stream() .map(this::convertToUserLocationDTO) .collect(Collectors.toList()); } private UserLocationDTO convertToUserLocationDTO(User user) { UserLocationDTO userLocationDTO = new UserLocationDTO(); userLocationDTO.setUserId(user.getId()); userLocationDTO.setUsername(user.getUsername()); Location location = user.getLocation(); userLocationDTO.setLat(location.getLat()); userLocationDTO.setLng(location.getLng()); userLocationDTO.setPlace(location.getPlace()); return userLocationDTO;
}

Beim Abrufen einer Liste von Users, wir konvertieren sie direkt neben ihren Location Informationen an UserLocationDTO Objekte. Wenn Sie diesen Dienst aufrufen, rufen wir diese Liste der DTOs ab.

Zum Schluss machen wir eine /map Endpunkt, damit jemand den Standort der Benutzer abrufen kann:

@RestController
public class MapController { @Autowired private MapService mapService; @GetMapping("/map") @ResponseBody public List<UserLocationDTO> getAllUsersLocation() { List <UserLocationDTO> usersLocation = mapService.getAllUsersLocation(); return usersLocation; }
}

Dieser Endpunkt gibt nur a zurück @ResponseBody. Es kann entweder von einem Benutzer oder von einem anderen Dienst aufgerufen werden, der die Ergebnisse analysiert.

Laden wir unsere Datenbank mit einigen Dummy-Informationen zu Testzwecken hoch:

insert into location(id, lat, lng, place, description) values (1, 49.8, 24.03 ,'Lviv', 'Lviv is one of the largest and the most beautiful cities of Ukraine.');
insert into user(id, username, first_name, last_name, password, location_id) values (1, 'Romeo', 'Romeo', 'Montagues' ,'gjt6lf2nt5os', 1);
insert into user(id, username, first_name, last_name, password, location_id) values (2, 'Juliet', 'Juliet', 'Capulets' ,'s894mjg03hd0', 1);

Um unseren Endpunkt zu testen, verwenden wir ein Tool wie Postman, um unsere Endpunkte zu erreichen:

Postbotenergebnisse des Endpunkts der Federstiefelruhe

Großartig! Eine Liste unserer Benutzer wird nur mit den erforderlichen Informationen zurückgegeben, die sowohl übertragen als auch angezeigt werden.

Wir haben eine Mapping-Methode in unsere geschrieben MapService Durch die Aggregation und Konvertierung von Daten kann dieser Prozess jedoch problemlos automatisiert werden.

Zuordnung mit ModelMapper

ModelMapper ist eine großartige Mapping-Bibliothek, mit der wir zwischen Modellen und DTOs abbilden können. Es erleichtert die Objektzuordnung, indem automatisch festgelegt wird, wie ein Objektmodell einem anderen zugeordnet wird.

Um es einem Maven-Projekt hinzuzufügen, fügen wir die Abhängigkeit hinzu:

<dependency> <groupId>org.modelmapper</groupId> <artifactId>modelmapper</artifactId> <version>${version}</version>
</dependency>

Oder wenn Sie Gradle verwenden:

compile group: 'org.modelmapper', name: 'modelmapper', version: '${version}'

Aktualisieren wir unser vorheriges Beispiel mit der ModelMapper-Bibliothek:

@Service
public class MapService { @Autowired private UserRepository userRepository; @Autowired private ModelMapper modelMapper; public List<UserLocationDTO> getAllUsersLocation() { return ((List<User>) userRepository .findAll()) .stream() .map(this::convertToUserLocationDTO) .collect(Collectors.toList()); } private UserLocationDTO convertToUserLocationDTO(User user) { modelMapper.getConfiguration() .setMatchingStrategy(MatchingStrategies.LOOSE); UserLocationDTO userLocationDTO = modelMapper .map(user, UserLocationDTO.class); return userLocationDTO; }
}

Anstelle des gesamten Zuweisungsprozesses, den wir zuvor durchführen mussten, tun wir dies einfach map() a user zu den UserLocationDTO. Die Methode reduziert die Eigenschaften von User innerhalb eines UserLocationDTO und sowohl die Benutzerinformationen als auch der Ort werden vorhanden sein.

Hinweis: Wenn Sie mit Objekten als Eigenschaften arbeiten, wie z Location ist eine Eigenschaft von UserDer Standard-Matcher der Bibliothek kann möglicherweise nicht alle Eigenschaften abgleichen. Wir haben die Matching-Strategie auf festgelegt LOOSE um der Bibliothek das Auffinden und Abgleichen von Eigenschaften zu erleichtern.

Zuordnung mit MapStruct

Kartenstruktur ist ein Open-Source-Java-basierter Codegenerator, der Code für Mapping-Implementierungen erstellt.

Es verwendet die Annotation-Verarbeitung, um Mapper-Klassen-Implementierungen während der Kompilierung zu generieren, und reduziert die Menge an Boilerplate-Code, der regelmäßig von Hand geschrieben wird, erheblich.

Wenn Sie Maven verwenden, installieren Sie MapStruct, indem Sie die Abhängigkeit hinzufügen:

<dependencies> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> <version>${org.mapstruct.version}</version> </dependency>
</dependencies>

Diese Abhängigkeit importiert die MapStruct-Kernanmerkungen. Da MapStruct zur Kompilierungszeit arbeitet und an Builder wie Maven und Gradle angehängt ist, müssen wir dem auch ein Plugin hinzufügen <build>:

<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.5.1</version> <configuration> <source>1.8</source> <target>1.8</target> <annotationProcessorPaths> <path> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>${org.mapstruct.version}</version> </path> </annotationProcessorPaths> </configuration> </plugin> </plugins>
</build>

Wenn Sie mit GradleDie Installation von MapStruct ist so einfach wie:

plugins { id 'net.ltgt.apt' version '0.20'
} // Depending on your IDE
apply plugin: 'net.ltgt.apt-idea'
apply plugin: 'net.ltgt.apt-eclipse' dependencies { compile "org.mapstruct:mapstruct:${mapstructVersion}" annotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}"
}

Wir haben unsere User und Location Klassen schon, also lasst uns einen Mapper für diese machen:

@Mapper
public interface UserLocationMapper { UserLocationMapper INSTANCE = Mappers.getMapper(UserLocationMapper.class); @Mapping(source = "user.id", target = "userId") UserLocationDTO toDto(User user, Location location);
}

Wenn Sie das Projekt erstellen, übernimmt MapStruct dies @Mapper und erzeuge ein UserLocationMapperImpl Klasse mit einer voll funktionsfähigen Implementierung.

MapStruct bietet eine Vielzahl von Funktionen und erweiterte Funktionen. Wenn Sie mehr darüber lesen möchten, empfehlen wir Ihnen dringend, unsere ausführlichen Informationen zu lesen Anleitung zu MapStruct in Java.

Zusammenfassung

In diesem Artikel haben wir das Entwurfsmuster für Datenübertragungsobjekte mit seinen Vor- und Nachteilen überprüft. Dieses Muster ist nur für Remote-Anrufe vorgesehen, da die Konvertierung von und zu DTOs teuer sein kann.

Darüber hinaus haben wir eine Demo-Spring-Boot-Anwendung erstellt und zwei beliebte Mapper untersucht, mit denen das Mapping zwischen Modellen und DTOs vereinfacht werden kann.

Den gesamten Projektcode finden Sie unter GitHub.

Quelle: https://stackabuse.com/data-transfer-object-pattern-in-java-implementation-and-mapping/

spot_img

Neueste Intelligenz

spot_img