Рубрики
Без рубрики

Шаблон объекта передачи данных в Java – Реализация и сопоставление

В этом руководстве мы реализуем шаблон объекта передачи данных в приложении Java Spring Boot. Мы также рассмотрим примеры сопоставления сущностей с DTO.

Автор оригинала: Victoria Seniuk.

Вступление

Корпоративное приложение-это программное решение, созданное для нужд организации. Часто это крупномасштабная, многоуровневая, масштабируемая система. Корпоративное программное обеспечение может работать с большим количеством сложных данных, и для этого типа программного обеспечения важно иметь хорошую архитектуру.

Шаблоны архитектуры корпоративных приложений являются стандартизированными решениями распространенных проблем, возникающих в больших системах. Они развивают архитектурное мышление и помогают разработчикам быть более уверенными в создании систем с доказанной надежностью.

Корпоративным приложениям может быть поручено управлять, отображать или хранить огромные объемы данных. При работе с этими приложениями не следует забывать о том, чтобы избегать тесной связи и обеспечивать целостность/безопасность данных.

Объект Передачи Данных

Шаблон проектирования Объекта передачи данных является одним из шаблонов архитектуры корпоративных приложений, который требует использования объектов, которые объединяют и инкапсулируют данные для передачи. Объект передачи данных , по сути, подобен структуре данных. Он не должен содержать никакой бизнес-логики, но должен содержать механизмы сериализации и десериализации.

DTO могут содержать либо все данные из источника, либо частичные данные. Они также могут содержать данные из одного или нескольких источников. При внедрении, чтобы стать средством передачи данных между системами.

Мартин Фаулер описывает Объект передачи данных в своей знаменитой книге Шаблоны архитектуры корпоративных приложений . Там основная идея DTOs состоит в том, чтобы сократить количество дорогостоящих удаленных вызовов.

Мартин Фаулер также определяет объект ассемблера , используемый для преобразования данных между DTO и любыми объектами сущностей. В настоящее время мы используем картографы для этой цели.

Что стоит отметить, так это то, что применение шаблона объекта передачи данных может стать анти-шаблоном в локальных системах. Он предназначен для использования в удаленных вызовах для повышения безопасности и ослабления связи. Если применить это к локальным системам, то это просто перепроектирование простой функции.

Мотивация

Давайте предположим, что нам нужно разработать корпоративную систему для компании. Система будет включать базу данных с различной общей информацией о сотрудниках – зарплата, проекты, сертификаты, личные данные (адрес, семейное положение, номер телефона и т.д.).

Охрана на входе в компанию требует доступа к нашей системе, чтобы идентифицировать работника, который хочет войти. Им нужна какая-то элементарная информация, такая как фамилия и фотография работника.

Мы не хотим отправлять в систему безопасности другую конфиденциальную информацию, такую как личная информация. Это избыточно и подвергает канал связи между системами атакам. Мы предоставим только то, что необходимо, и объем данных будет определен в объявлении.

В приложениях Java – мы используем классы сущностей для представления таблиц в реляционной базе данных. Без DTO нам пришлось бы предоставлять все объекты удаленному интерфейсу. Это приводит к сильной связи между API и моделью сохранения.

Используя DTO для передачи только необходимой информации, мы ослабляем связь между API и нашей моделью, что позволяет нам легче поддерживать и масштабировать сервис.

Реализация объекта передачи данных

Давайте создадим приложение, которое позаботится об отслеживании местоположения ваших друзей. Мы создадим приложение Spring Boot, которое предоставляет API REST. Используя его, мы сможем извлекать местоположения пользователей из базы данных H2.

Если вы хотите прочитать об интеграции базы данных H2 с Spring Boot , мы вам поможем!

Настройка пружинной загрузки

Самый простой способ начать с чистого приложения Spring Boot-это использовать Spring Initializr :

Кроме того, вы также можете использовать интерфейс командной строки Spring Boot для начальной загрузки приложения:

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

Если у вас уже есть приложение Maven/Spring, добавьте зависимость в свой pom.xml файл:


    com.h2database
    h2
    ${version}

Или если вы используете Gradle:

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

Демонстрационное приложение

Давайте начнем с Пользователя модели:

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

Он содержит некоторую элементарную информацию, такую как имя пользователя , имя , адрес электронной почты и т.д. Он также имеет отношение “многие к одному” с Местоположением сущностью:

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

Для базовых CRUD операций мы будем полагаться на надежный CrudRepository , предоставляемый Spring Boot:

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

Если вы не уверены, как это работает, мы предлагаем прочитать наше руководство по Spring Data JPA . Короче говоря, они загрузят нас базовыми функциями CRUD для наших моделей.

На этом этапе мы хотели бы создать контроллер, который обрабатывает запрос GET и возвращает список местоположений пользователя. Хотя, если мы извлекем Пользователя и Местоположение объекты из нашей базы данных и просто напечатаем необходимую информацию – другая информация, такая как пароль, также будет содержаться в этом объекте. Мы не будем его печатать, но оно там будет.

Давайте создадим Объект передачи данных , чтобы передавать только необходимую информацию. И пока мы этим занимаемся, давайте объединим информацию о Пользователе и Местоположении , чтобы данные передавались вместе:

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

Git Essentials

Ознакомьтесь с этим практическим руководством по изучению Git, содержащим лучшие практики и принятые в отрасли стандарты. Прекратите гуглить команды Git и на самом деле изучите это!

Теперь этот объект содержит всю информацию, которую мы хотим показать конечному пользователю. Теперь нам понадобится способ сопоставить Пользователя и Местоположение объектов в одно Местоположение пользователя D ДЛЯ объекта. Обычно это делается с помощью инструментов отображения, таких как MapStruct или ModelMapper , которые мы рассмотрим в последних разделах.

А пока давайте выполним преобразование вручную. Поскольку нам понадобится служба , которая вызывает наш UserRepository , также отобразит результаты там и вернет 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;
}

Получив список Пользователей , мы напрямую преобразуем их вместе с их Местоположением информацией в Местоположение пользователя D В объекты. При вызове этой службы мы получим этот список DTO.

Наконец, давайте создадим конечную точку /map , чтобы кто-то мог получить местоположение пользователей:

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

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

Эта конечная точка просто возвращает @ResponseBody . Он может быть вызван пользователем или другой службой, которая анализирует результаты.

Давайте загрузим нашу базу данных некоторой фиктивной информацией для целей тестирования:

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

Как проверить нашу конечную точку, мы будем использовать такой инструмент, как Postman, чтобы попасть в наши конечные точки:

Отлично! Список наших пользователей возвращается только с необходимой информацией, как переданной, так и отображенной.

Мы написали метод отображения в вашем Картографическом сервисе , который объединяет и преобразует данные, благодаря чему этот процесс можно легко автоматизировать.

Сопоставление с помощью ModelMapper

ModelMapper – это отличная библиотека картографирования, которая позволяет нам сопоставлять модели и DTO. Это упрощает сопоставление объектов, автоматически определяя, как одна объектная модель сопоставляется с другой.

Чтобы добавить его в проект Maven, мы бы добавили зависимость:


    org.modelmapper
    modelmapper
    ${version}

Или, если вы используете Gradle:

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

Давайте обновим наш предыдущий пример с помощью библиотеки 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;
    }
}

Теперь вместо всего процесса назначения, который нам приходилось выполнять раньше, мы просто сопоставляем() пользователя с местоположением пользователя D В . Метод сгладит свойства Пользователя в пределах Местоположения пользователя D ДО , и будут присутствовать как информация о пользователе, так и местоположение.

Примечание: При работе с объектами в качестве свойств, например , наше Местоположение является свойством Пользователя , стандартные соответствия библиотеки могут не соответствовать всем свойствам. Мы установили стратегию сопоставления в LOOSE , чтобы библиотеке было проще находить и сопоставлять свойства.

Сопоставление с помощью структуры карты

MapStruct -это генератор кода на основе Java с открытым исходным кодом, который создает код для реализаций сопоставления.

Он использует обработку аннотаций для создания реализаций классов картографов во время компиляции и значительно сокращает объем шаблонного кода, который регулярно писался бы вручную.

Если вы используете Maven, установите MapStruct, добавив зависимость:


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

Эта зависимость будет импортировать аннотации основной структуры карты. Поскольку MapStruct работает во время компиляции и прикреплен к таким конструкторам, как Maven и Gradle, нам также придется добавить плагин в <сборка> :


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

Если вы используете Gradle , установка MapStruct так же проста, как:

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

У нас уже есть наши Пользователь и Местоположение классы, так что давайте сделаем картограф для них:

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

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

Когда вы создадите проект, MapStruct подберет этот @Mapper и сгенерирует Класс отображения местоположения пользователя с полностью функционирующей реализацией.

Структура карты обладает широким спектром функциональных возможностей и расширенным набором функций. Если вам интересно узнать об этом больше, мы настоятельно рекомендуем прочитать наше подробное руководство по структуре карт на Java .

Вывод

В этой статье мы рассмотрели Шаблон проектирования объекта передачи данных с его плюсами и минусами. Этот шаблон действительно предназначен только для удаленных вызовов, потому что преобразование из и в DTO может быть дорогостоящим.

Кроме того, мы создали демонстрационное приложение Spring Boot и изучили два популярных картографа, которые можно использовать для упрощения процесса сопоставления между моделями и DTO.

Вы можете найти весь код проекта на GitHub .