Приятно, что такая среда, как SpringBoot, может сделать за вас так много вещей.
Вам просто нужен класс сущности JPA плюс простой интерфейс репозитория, и SpringData предоставит вам все необходимое для типичных операций с базой данных CRUD.
Вы пишете простой класс контроллера REST и у вас работает REST API, верно?
Эй, но ты забыл написать DTO! Но зачем вам это вообще нужно, если ваше приложение может работать и без него?
Конечно, есть несколько общих причин:
Но могут случиться и другие странные вещи. Я покажу вам один странный пример, основанный на моем опыте.
Этот репозиторий GitHub содержит простое приложение, которое работает без DTO. Существует сущность «Пользователь», каждый пользователь может иметь несколько транзакций. У нас даже есть компонент Service между репозиторием и RestController, перехватывающий возможные исключения доступа к базе данных.
Поскольку мы хотим создать готовое к использованию приложение, мы не хотим, чтобы Hibernate генерировал DDL. Вместо этого у нас есть файл Schema.sql, который создает таблицы (позже мы можем переключиться на Flyway или Liquibase). В нашем простом примере у нас также есть data.sql, чтобы наши таблицы не были пустыми.
Когда мы запускаем приложение и вызываем конечную точку API по адресу http://localhost:8080/users, мы получаем ожидаемый JSON, содержащий пользователей и их транзакции.
Теперь обратим внимание на две строки кода в классе Transaction, помеченные //!!
@JsonIgnore //!!
Первый запах заключается в том, что в классе Transaction нам пришлось добавить аннотацию @JsonIgnore к ссылке на пользователя. Без этой аннотации сериализация JSON аварийно завершается из-за бесконечной рекурсии.
Теперь представим, что кто-то совершает ошибку, добавляя еще одно поле (описание) к сущности Transaction, но забывает скорректировать операторы SQL (или запускает приложение в среде, где изменение схемы не было применено).
]
описание частной строки;//!!
Конечно, теперь вызов API завершается неудачно. Но посмотрите на обработку ошибок! Предложение catch внутри UserService не работает должным образом. Вместо этого мы видим в журнале странную трассировку стека:
GlobalExceptionHandler: непредвиденная ошибка org.springframework.http.converter.HttpMessageNotWritableException: не удалось записать JSON:
Однажды я видел эту ситуацию (очевидно, с приложением, намного большим, чем этот пример), и мне потребовалось немало времени, чтобы понять, почему исключение SQL ускользнуло от службы и почему я получил исключение HttpMessageNotWritableException. Видишь?
Происходит следующее: класс UserService (через UserRepository) запрашивает только таблицу базы данных USERS. Объекты транзакции не являются частью результата из-за отложенной загрузки Hibernate по умолчанию. Только когда десериализатор Джексона пытается создать JSON из экземпляра User, он вызывает метод getTransactions, который заставляет Hibernate извлекать объекты транзакции.
Вот почему мы получаем странную трассировку стека, объединяющую данные JSON и SQL. Исключение перехватывается GlobalExceptionHandler, который не знает, что с ним делать, поэтому в журнале появляется сообщение «Неожиданная ошибка».
Я надеюсь, что это небольшое упражнение поможет вам глубже понять, насколько опасно смешивать разные уровни вашего приложения. Видение сценариев «солнечного дня» вашего приложения, пока оно еще маленькое, может привести к тому, что некоторые разработчики будут продолжать делать неправильные вещи, пока не станет слишком поздно.
Вам не нужно писать шаблонный код, сопоставляющий поля между вашим DTO и другими уровнями вашего приложения. MapStruct может сделать это за вас.
Отказ от ответственности: Все предоставленные ресурсы частично взяты из Интернета. В случае нарушения ваших авторских прав или других прав и интересов, пожалуйста, объясните подробные причины и предоставьте доказательства авторских прав или прав и интересов, а затем отправьте их по электронной почте: [email protected]. Мы сделаем это за вас как можно скорее.
Copyright© 2022 湘ICP备2022001581号-3