Modèle d’objet de transfert de données en Java

Online Coding Courses for Kids

introduction

Une application d’entreprise est une solution logicielle créée pour les besoins d’une organisation. Il s’agit souvent d’un système à grande échelle, à plusieurs niveaux et évolutif. Les logiciels d’entreprise peuvent traiter de nombreuses données complexes et il est important que ce type de logiciel ait une bonne architecture.

Modèles d’architecture d’application d’entreprise sont des solutions standardisées aux problèmes courants rencontrés dans les grands systèmes. Ils cultivent la pensée architecturale et aident les développeurs à être plus confiants dans la construction de systèmes avec une fiabilité éprouvée.

Les applications d’entreprise peuvent être chargées de manipuler, d’afficher ou de stocker d’énormes quantités de données. Éviter un couplage serré et garantir l’intégrité / la sécurité des données ne doit pas être une réflexion après coup lorsque vous travaillez sur ces applications.

Objet de transfert de données

le Modèle de conception d’objet de transfert de données est l’un des modèles d’architecture d’application d’entreprise qui nécessite l’utilisation d’objets qui regroupent et encapsulent les données à transférer. UNE Objet de transfert de données est, essentiellement, comme une structure de données. Il ne doit contenir aucune logique métier mais doit contenir des mécanismes de sérialisation et de désérialisation.

Les DTO peuvent contenir toutes les données d’une source ou des données partielles. Ils peuvent également contenir des données provenant d’une ou de plusieurs sources. Une fois mis en œuvre, les DTO deviennent le moyen de transport de données entre les systèmes.

Martin Fowler décrit le Objet de transfert de données dans son célèbre livre Modèles d’architecture d’application d’entreprise. Là, l’idée principale de DTO est de réduire le nombre d’appels à distance qui sont chers.

Martin Fowler définit également un objet assembleur, utilisé pour convertir les données entre le DTO et tout objet entité. De nos jours, nous utilisons mappeurs Dans ce but.

Il convient de noter que l’application du modèle d’objet de transfert de données peut devenir un anti-modèle dans les systèmes locaux. Il est destiné à être utilisé dans les appels distants pour promouvoir la sécurité et le couplage lâche. S’il est appliqué aux systèmes locaux, il s’agit simplement de sur-concevoir une fonctionnalité simple.

Motivation

Supposons que nous devons développer un système d’entreprise pour une entreprise. Le système comprendra une base de données contenant diverses informations générales sur les employés – salaire, projets, certificats, données personnelles (adresse, situation familiale, numéro de téléphone, etc.).

La sécurité à l’entrée de l’entreprise nécessite l’accès à notre système, pour identifier le travailleur qui veut entrer. Ils ont besoin d’informations rudimentaires, telles que le nom et la photo du travailleur.

nous ne le fais pas souhaitez envoyer d’autres informations sensibles au système de sécurité, telles que des informations personnelles. Il est redondant et expose le canal de communication entre les systèmes aux attaques. Nous ne fournirons que ce qui est nécessaire, et la portée des données sera définie dans un DTO.

Dans les applications Java – nous utilisons des classes d’entités pour représenter des tables dans une base de données relationnelle. Sans DTO, nous aurions à exposer l’ensemble des entités à une interface distante. Cela provoque un fort couplage entre une API et un modèle de persistance.

En utilisant un DTO pour transférer uniquement les informations requises, nous desserrons le couplage entre l’API et notre modèle, ce qui nous permet de maintenir et de dimensionner plus facilement le service.

Implémentation d’un objet de transfert de données

Faisons une application qui s’occupe du suivi de localisation pour vos amis. Nous allons créer une application Spring Boot qui expose une API REST. En l’utilisant, nous pourrons récupérer les emplacements des utilisateurs à partir d’une base de données H2.

Si vous souhaitez lire sur l’intégration d’une base de données H2 avec Spring Boot, nous avons ce qu’il vous faut!

Configuration de Spring Boot

La façon la plus simple de commencer avec une application Spring Boot vierge est d’utiliser Spring Initializr:

Spring boot initializr

Vous pouvez également utiliser le CLI Spring Boot pour amorcer l’application:

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

Si vous avez déjà une application Maven / Spring, ajoutez la dépendance à votre pom.xml fichier:


    com.h2database
    h2
    ${version}

Ou si vous utilisez Gradle:

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

Application de démonstration

Commençons par le User modèle:

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

Il contient des informations rudimentaires comme username, firstName, email, etc. Il a également une relation de plusieurs à un avec le Location entité:

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

Pour basique CRUD opérations, nous comptons sur les fidèles CrudRepository fourni par Spring Boot:

@Repository
public interface UserRepository extends CrudRepository{}
@Repository
public interface LocationRepository extends CrudRepository {}

Si vous ne savez pas comment cela fonctionne, nous vous suggérons de lire notre Guide de Spring Data JPA. En bref, ils nous amèneront avec la fonctionnalité CRUD de base pour nos modèles.

À ce stade, nous voudrions faire un contrôleur qui gère un AVOIR demande et renvoie une liste des emplacements de l’utilisateur. Cependant, si nous récupérons User et Location objets de notre base de données, et imprimez simplement les informations requises – les autres informations, telles que le mot de passe seront également contenues dans cet objet. Nous ne l’imprimerons pas, mais il sera là.

Faisons un Objet de transfert de données pour transférer uniquement les informations requises. Et pendant que nous y sommes, agrégons le User et Location informations, afin que les données soient transférées ensemble:

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

Cet objet contient maintenant toutes les informations que nous voulons montrer à l’utilisateur final. Maintenant, nous aurons besoin d’un moyen de carte le User et Location objets en un seul UserLocationDTO objet. Cela se fait généralement via des outils de cartographie, tels que MapStruct ou ModelMapper, que nous explorerons dans les dernières sections.

Pour l’instant, exécutons la conversion manuellement. Puisque nous aurons besoin d’un service qui appelle notre UserRepository, nous y cartographierons également les résultats et renverrons les DTO:

@Service
public class MapService {

    @Autowired
    private UserRepository userRepository;

    public List getAllUsersLocation() {
        return ((List) 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;
}

Lors de la récupération d’une liste de Users, nous les convertissons directement, aux côtés de leurs Location informations à UserLocationDTO objets. Lorsque vous appelez ce service, nous récupérons cette liste de DTO.

Enfin, faisons un /map point de terminaison pour permettre à quelqu’un de récupérer l’emplacement des utilisateurs:

@RestController
public class MapController {
  
    @Autowired
    private MapService mapService;

    @GetMapping("/map")
    @ResponseBody
    public List getAllUsersLocation() {
        List  usersLocation = mapService.getAllUsersLocation();
        return usersLocation;
    }
}

Ce point final renvoie juste un @ResponseBody. Il peut être appelé par un utilisateur ou par un autre service qui analyse les résultats.

Chargeons notre base de données avec des informations factices à des fins de test:

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

Maintenant, pour tester notre point de terminaison, nous allons utiliser un outil comme Postman pour atteindre nos points de terminaison:

résultats du facteur du point final du repos de la botte de printemps

Génial! Une liste de nos utilisateurs est retournée avec uniquement les informations requises à la fois transférées et affichées.

Nous avons écrit une méthode de cartographie dans notre MapService qui agrège et convertit les données, cependant, ce processus peut facilement être automatisé.

Cartographie avec ModelMapper

ModelMapper est une excellente bibliothèque de mappage qui nous permet de mapper entre les modèles et les DTO. Il facilite le mappage d’objets, en déterminant automatiquement comment un modèle d’objet est mappé à un autre.

Pour l’ajouter à un projet Maven, nous ajouterions la dépendance:


    org.modelmapper
    modelmapper
    ${version}

Ou, si vous utilisez Gradle:

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

Mettons à jour notre exemple précédent avec la bibliothèque ModelMapper:

@Service
public class MapService {

    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private ModelMapper modelMapper;

    public List getAllUsersLocation() {
       return ((List) 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;
    }
}

Maintenant, au lieu de l’ensemble du processus d’affectation que nous avons dû faire avant – nous venons de map() une user à la UserLocationDTO. La méthode aplatira les propriétés de User dans un UserLocationDTO et les informations utilisateur et l’emplacement seront présents.

Remarque: Lorsque vous travaillez avec des objets en tant que propriétés, comme notre Location est une propriété de User, la correspondance standard de la bibliothèque peut ne pas correspondre à toutes les propriétés. Nous avons défini la stratégie de correspondance sur LOOSE pour faciliter la recherche et la mise en correspondance des propriétés par la bibliothèque.

Cartographie avec MapStruct

MapStruct est un générateur de code open-source basé sur Java qui crée du code pour les implémentations de mappage.

Il utilise le traitement d’annotation pour générer des implémentations de classe de mappeur pendant la compilation et réduit considérablement la quantité de code standard qui serait régulièrement écrit à la main.

Si vous utilisez Maven, installez MapStruct en ajoutant la dépendance:


    
        org.mapstruct
        mapstruct
        ${org.mapstruct.version}
    

Cette dépendance importera les annotations principales de MapStruct. Puisque MapStruct fonctionne à la compilation et est attaché à des constructeurs comme Maven et Gradle, nous devrons également ajouter un plugin à la :


    
        
            org.apache.maven.plugins
            maven-compiler-plugin
            3.5.1
            
                1.8
                1.8
                
                    
                        org.mapstruct
                        mapstruct-processor
                        ${org.mapstruct.version}
                    
                
            
        
    

Si vous utilisez Gradle, l’installation de MapStruct est aussi simple que:

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

Nous avons notre User et Location classes déjà, alors faisons un mappeur pour ceux-ci:

@Mapper
public interface UserLocationMapper {
    UserLocationMapper INSTANCE = Mappers.getMapper(UserLocationMapper.class);

    @Mapping(source = "user.id", target = "userId")
    UserLocationDTO toDto(User user, Location location);
}

Lorsque vous générez le projet, MapStruct récupérera ce @Mapper et générer un UserLocationMapperImpl avec une implémentation pleinement fonctionnelle.

MapStruct possède une grande variété de fonctionnalités et un ensemble avancé de fonctionnalités. Si vous souhaitez en savoir plus, nous vous suggérons fortement de lire notre Guide détaillé de MapStruct en Java.

Conclusion

Dans cet article, nous avons examiné le modèle de conception d’objet de transfert de données avec ses avantages et ses inconvénients. Ce modèle est vraiment dédié uniquement aux appels distants car la conversion depuis et vers les DTO peut être coûteuse.

De plus, nous avons créé une application de démonstration Spring Boot et exploré deux mappeurs populaires qui peuvent être utilisés pour simplifier le processus de mappage entre les modèles et les DTO.

Vous pouvez trouver tout le code du projet sur GitHub.

Close Menu