Я использую весеннюю загрузку с mysql для создания Restful API. Вот пример того, как я возвращаю ответ json.
сначала у меня есть модель:
@Entity
public class Movie extends DateAudit {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Date releaseDate;
private Time runtime;
private Float rating;
private String storyline;
private String poster;
private String rated;
@OneToMany(mappedBy = "movie", cascade = CascadeType.ALL, orphanRemoval = true)
private List<MovieMedia> movieMedia = new ArrayList<>();
@OneToMany(mappedBy = "movie", cascade = CascadeType.ALL, orphanRemoval = true)
private List<MovieReview> movieReviews = new ArrayList<>();
@OneToMany(mappedBy = "movie", cascade = CascadeType.ALL, orphanRemoval = true)
private List<MovieCelebrity> movieCelebrities = new ArrayList<>();
// Setters & Getters
}
и соответствующий репозиторий:
@Repository
public interface MovieRepository extends JpaRepository<Movie, Long> {
}
Также у меня есть класс полезной нагрузки MovieResponse, который представляет фильм вместо модели Кино, и это, например, если мне нужны дополнительные поля или мне нужно вернуть определенные поля.
public class MovieResponse {
private Long id;
private String name;
private Date releaseDate;
private Time runtime;
private Float rating;
private String storyline;
private String poster;
private String rated;
private List<MovieCelebrityResponse> cast = new ArrayList<>();
private List<MovieCelebrityResponse> writers = new ArrayList<>();
private List<MovieCelebrityResponse> directors = new ArrayList<>();
// Constructors, getters and setters
public void setCelebrityRoles(List<MovieCelebrityResponse> movieCelebrities) {
this.setCast(movieCelebrities.stream().filter(movieCelebrity -> movieCelebrity.getRole().equals(CelebrityRole.ACTOR)).collect(Collectors.toList()));
this.setDirectors(movieCelebrities.stream().filter(movieCelebrity -> movieCelebrity.getRole().equals(CelebrityRole.DIRECTOR)).collect(Collectors.toList()));
this.setWriters(movieCelebrities.stream().filter(movieCelebrity -> movieCelebrity.getRole().equals(CelebrityRole.WRITER)).collect(Collectors.toList()));
}
}
Как видите, я разделил список знаменитостей фильма на 3 списка (актеры, режиссеры и сценаристы).
И чтобы сопоставить Кино с MovieResponse, я использую класс ModelMapper:
public class ModelMapper {
public static MovieResponse mapMovieToMovieResponse(Movie movie) {
// Create a new MovieResponse and Assign the Movie data to MovieResponse
MovieResponse movieResponse = new MovieResponse(movie.getId(), movie.getName(), movie.getReleaseDate(),
movie.getRuntime(),movie.getRating(), movie.getStoryline(), movie.getPoster(), movie.getRated());
// Get MovieCelebrities for current Movie
List<MovieCelebrityResponse> movieCelebrityResponses = movie.getMovieCelebrities().stream().map(movieCelebrity -> {
// Get Celebrity for current MovieCelebrities
CelebrityResponse celebrityResponse = new CelebrityResponse(movieCelebrity.getCelebrity().getId(),
movieCelebrity.getCelebrity().getName(), movieCelebrity.getCelebrity().getPicture(),
movieCelebrity.getCelebrity().getDateOfBirth(), movieCelebrity.getCelebrity().getBiography(), null);
return new MovieCelebrityResponse(movieCelebrity.getId(), movieCelebrity.getRole(),movieCelebrity.getCharacterName(), null, celebrityResponse);
}).collect(Collectors.toList());
// Assign movieCelebrityResponse to movieResponse
movieResponse.setCelebrityRoles(movieCelebrityResponses);
return movieResponse;
}
}
и, наконец, вот моя служба MovieService, которую я вызываю в контроллере:
@Service
public class MovieServiceImpl implements MovieService {
private MovieRepository movieRepository;
@Autowired
public void setMovieRepository(MovieRepository movieRepository) {
this.movieRepository = movieRepository;
}
public PagedResponse<MovieResponse> getAllMovies(Pageable pageable) {
Page<Movie> movies = movieRepository.findAll(pageable);
if (movies.getNumberOfElements() == 0) {
return new PagedResponse<>(Collections.emptyList(), movies.getNumber(),
movies.getSize(), movies.getTotalElements(), movies.getTotalPages(), movies.isLast());
}
List<MovieResponse> movieResponses = movies.map(ModelMapper::mapMovieToMovieResponse).getContent();
return new PagedResponse<>(movieResponses, movies.getNumber(),
movies.getSize(), movies.getTotalElements(), movies.getTotalPages(), movies.isLast());
}
}
Итак, вопрос: можно ли использовать для каждой модели, у меня есть класс полезной нагрузки для сериализации json? или там способ получше. также, ребята, это что-то не так с моим кодом, не стесняйтесь комментировать.




Мы должны отделить каждый слой от другого. Как и в вашем случае, вы определили классы сущности и ответа. Это правильный способ разделения вещей, мы никогда не должны отправлять сущность в ответ. Даже для запроса у нас должен быть класс.
В чем проблема, если мы отправляем объект вместо ответа dto.
Невозможно изменить их, потому что мы уже предоставляем его нашему клиенту.
Иногда мы не хотим сериализовать некоторые поля и отправлять их в качестве ответа.
Некоторые накладные расходы необходимы для перевода запроса в домен, объекта в домен и т.д. ModelMapper - лучший выбор для перевода.
Попробуйте использовать внедрение конструкции вместо установщика для зависимости мандата.
Всегда рекомендуется разделять DTO и Entity. Сущность должен взаимодействовать с БД / ORM, а DTO должен взаимодействовать с клиентским уровнем (уровень для запроса и ответа), даже если структура Сущность и DTO одинакова.
Здесь Entity - Movie и
DTO - это MovieResponse
Используйте существующий класс MovieResponse для запроса и ответа.
Никогда не используйте класс Movie для запроса и ответа.
а класс MovieServiceImpl должен содержать бизнес-логику для преобразования Сущность в DTO, или вы можете использовать Бульдозер api для автоматического преобразования.
Причина расслоения:
DTO - это шаблон проектирования, который решает проблему получения максимально возможных полезных данных из службы.
В случае такого простого приложения, как ваше, DTO, как правило, похожи на классы Entity. Однако для некоторых сложных приложений DTO могут быть расширены для объединения данных от различных объектов, чтобы избежать множественных запросов к серверу и, таким образом, сэкономить ценные ресурсы и время запроса-ответа.
Я бы посоветовал не дублировать код в таком простом случае, а также использовать классы моделей в ответ на API. Использование отдельных классов ответа в качестве DTO не решит никаких задач и только усложнит поддержку кода.
С одной стороны, вы должны разделить их, потому что иногда некоторые аннотации JPA, которые вы используете в своей модели, не работают с аннотациями процессора json. И да, вы должны держать вещи отдельно.
Что, если позже вы решите изменить уровень данных? Придется ли вам переписывать всю клиентскую часть?
С другой стороны, есть проблема отображения. Для этого вы можете использовать библиотеку с небольшим снижением производительности.
Не так давно у меня была эта дилемма, это был мой мыслительный процесс. Он у меня тут https://stackoverflow.com/questions/44572188/microservices-restful-api-dtos-or-not
Чем меньше кода вы напишете, тем меньше ошибок вы создадите.
Ремонтопригодность - меньше кода котельной плиты.
DTO дадут вам больше гибкости в долгосрочной перспективе
Объекты домена раздуваются аннотациями.
mybatis.configuration.map-underscore-to-camel-case: true, spring.jackson.property-naming-strategy: SNAKE_CASE.Короткий рассказ, по крайней мере, в моем случае, минусы не перевешивают плюсы, поэтому не имело смысла повторяться, имея новый POJO в качестве DTO. Меньше кода, меньше шансов на ошибку. Итак, продолжаем выставлять объект «Домен» и не иметь отдельного объекта «представления».
Отказ от ответственности: это может или не может быть применимо в вашем случае использования. Это наблюдение соответствует моему варианту использования (в основном API CRUD с 15-ю конечными точками)
Хотя большинство людей ответили за и против использования объектов DTO, я хотел бы отдать свои 2 цента. В моем случае DTO был необходим, потому что не все поля, сохраненные в базе данных, были захвачены от пользователя. Было несколько полей, которые были вычислены на основе пользовательского ввода (других полей) и не были доступны пользователям. Кроме того, он также может уменьшить размер полезной нагрузки, что может привести к повышению производительности в таких случаях.
Я выступаю за отделение объекта «Полезная нагрузка» или «Данные» от объекта «Модель» или «Отображение». Практически всегда. Это просто упрощает управление.
Вот пример: Допустим, вам нужно найти API, который предоставляет данные о выставленных на продажу кошках. Затем вы анализируете данные в объект модели кошки и заполняете список кошек, который затем отображается пользователю. Прохладный.
Но теперь вы хотите интегрировать другой API и вытаскивать кошек из двух баз данных. Но вы столкнетесь с проблемой. Один API возвращает furColor для цвета, а новый возвращает catColor для цвета.
Если вы использовали тот же объект для отображения информации, у вас есть несколько вариантов:
furColor, так и catColor, сделайте их необязательными и выполните какое-то вычисленное свойство, чтобы проверить, какое из них установлено, и использовать его для отображения цвета.
Однако, если вы создаете объект данных, который улавливает ответ, а затем экранный объект, который имеет только информацию, необходимую для заполнения списка, это становится действительно простым:
Это также сделает хранение данных локально более чистым.