From dc328de6ff00f879f291aa0f3163065683121a1f Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 8 Oct 2026 16:54:34 +0300 Subject: [PATCH 1/6] add film directors --- .../controller/DirectorController.java | 52 +++++++++ .../filmorate/controller/FilmController.java | 8 ++ .../practicum/filmorate/model/Director.java | 10 ++ .../practicum/filmorate/model/Film.java | 2 + .../filmorate/service/DirectorService.java | 39 +++++++ .../filmorate/service/FilmService.java | 4 + .../storage/director/DirectorDbStorage.java | 68 +++++++++++ .../storage/director/DirectorStorage.java | 18 +++ .../filmorate/storage/film/FilmDbStorage.java | 107 +++++++++++++++++- .../filmorate/storage/film/FilmStorage.java | 2 + .../storage/mapper/DirectorRowMapper.java | 19 ++++ src/main/resources/schema.sql | 12 ++ .../director/DirectorDbStorageTest.java | 75 ++++++++++++ .../storage/film/FilmDbStorageTest.java | 64 ++++++++++- 14 files changed, 471 insertions(+), 9 deletions(-) create mode 100644 src/main/java/ru/yandex/practicum/filmorate/controller/DirectorController.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/model/Director.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/service/DirectorService.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/storage/director/DirectorDbStorage.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/storage/director/DirectorStorage.java create mode 100644 src/main/java/ru/yandex/practicum/filmorate/storage/mapper/DirectorRowMapper.java create mode 100644 src/test/java/ru/yandex/practicum/filmorate/storage/director/DirectorDbStorageTest.java diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/DirectorController.java b/src/main/java/ru/yandex/practicum/filmorate/controller/DirectorController.java new file mode 100644 index 0000000..2747dbe --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/DirectorController.java @@ -0,0 +1,52 @@ +package ru.yandex.practicum.filmorate.controller; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import ru.yandex.practicum.filmorate.model.Director; +import ru.yandex.practicum.filmorate.service.DirectorService; +import java.util.List; + +@Slf4j +@RestController +@RequestMapping("/directors") +public class DirectorController { + + private final DirectorService directorService; + + @Autowired + public DirectorController(DirectorService directorService) { + this.directorService = directorService; + } + + @GetMapping + public List getDirectors() { + log.info("GET /directors"); + return directorService.getAllDirectors(); + } + + @GetMapping("/{id}") + public Director getDirectorById(@PathVariable Long id) { + log.info("GET /directors/{}", id); + return directorService.getDirectorById(id); + } + + @PostMapping + public Director createDirector(@RequestBody Director director) { + log.info("POST /directors/{}", director); + return directorService.addDirector(director); + } + + @PutMapping + public Director updateDirector(@RequestBody Director director) { + log.info("PUT /directors/{}", director); + return directorService.updateDirector(director); + } + + @DeleteMapping("/{id}") + public void deleteDirector(@PathVariable Long id) { + log.info("DELETE /directors/{}", id); + directorService.deleteDirector(id); + } + +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java index db89073..2085d18 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java @@ -71,4 +71,12 @@ public Collection getCommon(@RequestParam long userId, @RequestParam long log.info("GET /films/common?userId={}&friendId={}", userId, friendId); return filmService.getCommon(userId, friendId); } + + @GetMapping("/director/{directorId}") + public Collection getFilmsByDirector(@PathVariable long directorId, + @RequestParam (defaultValue = "likes") String sortBy) { + log.info("GET /films/director/{}?sortBy={}", directorId, sortBy); + return filmService.findByDirectorSorted(directorId, sortBy); + + } } \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/Director.java b/src/main/java/ru/yandex/practicum/filmorate/model/Director.java new file mode 100644 index 0000000..ed2dad9 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/model/Director.java @@ -0,0 +1,10 @@ +package ru.yandex.practicum.filmorate.model; + +import lombok.Data; + +@Data +public class Director { + + private Long id; + private String name; +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/Film.java b/src/main/java/ru/yandex/practicum/filmorate/model/Film.java index 08a9aca..795626f 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/model/Film.java +++ b/src/main/java/ru/yandex/practicum/filmorate/model/Film.java @@ -45,4 +45,6 @@ public void addLike(Long userId) { public boolean removeLike(Long userId) { return likes.remove(userId); } + + private Set directors = new LinkedHashSet<>(); } diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/DirectorService.java b/src/main/java/ru/yandex/practicum/filmorate/service/DirectorService.java new file mode 100644 index 0000000..c028138 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/service/DirectorService.java @@ -0,0 +1,39 @@ +package ru.yandex.practicum.filmorate.service; + +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Service; +import ru.yandex.practicum.filmorate.exception.NotFoundException; +import ru.yandex.practicum.filmorate.model.Director; +import ru.yandex.practicum.filmorate.storage.director.DirectorStorage; + +@Service +public class DirectorService { + + private final DirectorStorage directorStorage; + + public DirectorService(@Qualifier("directorDbStorage") DirectorStorage directorStorage) { + this.directorStorage = directorStorage; + } + + public Director addDirector(Director director) { + return directorStorage.addDirector(director); + } + + public Director updateDirector(Director director) { + return directorStorage.updateDirector(director); + } + + public void deleteDirector(long id) { + directorStorage.removeDirector(id); + } + + public Director getDirectorById(long id) { + return directorStorage.findDirectorById(id) + .orElseThrow(() -> new NotFoundException("Director with id " + id + " not found")); + } + + public java.util.List getAllDirectors() { + return directorStorage.findAllDirectors(); + } + +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java b/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java index 94089cb..37b8b50 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java +++ b/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java @@ -154,4 +154,8 @@ public Collection findAll() { public Film findById(long id) { return getFilmOrThrow(id); } + + public Collection findByDirectorSorted(long directorId, String sortBy) { + return filmStorage.findFilmByDirector(directorId, sortBy); + } } \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/director/DirectorDbStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/director/DirectorDbStorage.java new file mode 100644 index 0000000..fa4eb7c --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/director/DirectorDbStorage.java @@ -0,0 +1,68 @@ +package ru.yandex.practicum.filmorate.storage.director; + +import java.util.Optional; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.support.GeneratedKeyHolder; +import org.springframework.jdbc.support.KeyHolder; +import org.springframework.stereotype.Repository; +import ru.yandex.practicum.filmorate.exception.NotFoundException; +import ru.yandex.practicum.filmorate.model.Director; +import ru.yandex.practicum.filmorate.storage.mapper.DirectorRowMapper; +import java.util.List; + +@Repository +public class DirectorDbStorage implements DirectorStorage { + + private final JdbcTemplate jdbcTemplate; + private final DirectorRowMapper directorRowMapper; + + private static final String INSERT_QUERY = "INSERT INTO directors (name) VALUES (?)"; + private static final String UPDATE_QUERY = "UPDATE directors SET name = ? WHERE id = ?"; + private static final String DELETE_QUERY = "DELETE FROM directors WHERE id = ?"; + private static final String SELECT_BY_ID_QUERY = "SELECT * FROM directors WHERE id = ?"; + private static final String SELECT_ALL_QUERY = "SELECT * FROM directors"; + + @Autowired + public DirectorDbStorage(JdbcTemplate jdbcTemplate, DirectorRowMapper directorRowMapper) { + this.jdbcTemplate = jdbcTemplate; + this.directorRowMapper = directorRowMapper; + } + + @Override + public Director addDirector(Director director) { + KeyHolder keyHolder = new GeneratedKeyHolder(); + jdbcTemplate.update(connection -> { + var ps = connection.prepareStatement(INSERT_QUERY, new String[]{"id"}); + ps.setString(1, director.getName()); + return ps; + }, keyHolder); + director.setId(keyHolder.getKey().longValue()); + return director; + } + + @Override + public Director updateDirector(Director director) { + jdbcTemplate.update(UPDATE_QUERY, director.getName(), director.getId()); + return director; + } + + @Override + public void removeDirector(long id) { + Director director = findDirectorById(id) + .orElseThrow(() -> new NotFoundException("Director not found with id: " + id)); + jdbcTemplate.update(DELETE_QUERY, id); + } + + @Override + public Optional findDirectorById(long id) { + Optional director = jdbcTemplate.query(SELECT_BY_ID_QUERY, directorRowMapper, id).stream().findFirst(); + return director; + } + + @Override + public List findAllDirectors() { + List directors = jdbcTemplate.query(SELECT_ALL_QUERY, directorRowMapper); + return directors; + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/director/DirectorStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/director/DirectorStorage.java new file mode 100644 index 0000000..cdcdc40 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/director/DirectorStorage.java @@ -0,0 +1,18 @@ +package ru.yandex.practicum.filmorate.storage.director; + +import ru.yandex.practicum.filmorate.model.Director; +import java.util.List; +import java.util.Optional; + +public interface DirectorStorage { + + Director addDirector(Director director); + + Director updateDirector(Director director); + + void removeDirector(long id); + + Optional findDirectorById(long id); + + List findAllDirectors(); +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorage.java index 5280bef..8e898c3 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorage.java +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorage.java @@ -6,11 +6,13 @@ import org.springframework.jdbc.support.KeyHolder; import org.springframework.stereotype.Repository; import ru.yandex.practicum.filmorate.exception.NotFoundException; +import ru.yandex.practicum.filmorate.exception.ValidationException; +import ru.yandex.practicum.filmorate.model.Director; import ru.yandex.practicum.filmorate.model.Film; import ru.yandex.practicum.filmorate.model.Genre; +import ru.yandex.practicum.filmorate.storage.mapper.DirectorRowMapper; import ru.yandex.practicum.filmorate.storage.mapper.FilmRowMapper; import ru.yandex.practicum.filmorate.storage.mapper.GenreRowMapper; - import java.sql.Date; import java.sql.PreparedStatement; import java.sql.Statement; @@ -76,16 +78,43 @@ public class FilmDbStorage implements FilmStorage { "MERGE INTO film_likes (film_id, user_id) KEY (film_id, user_id) VALUES (?, ?)"; private static final String REMOVE_LIKE_QUERY = "DELETE FROM film_likes WHERE film_id = ? AND user_id = ?"; + private static final String INSERT_FILM_DIRECTOR_QUERY = + "INSERT INTO film_director (film_id, director_id) VALUES (?, ?)"; + private static final String DELETE_DIRECTOR_FILM_QUERY = + "DELETE FROM film_director WHERE film_id = ?"; + private static final String FIND_DIRECTORS_BY_FILM_QUERY = + "SELECT d.id, d.name FROM film_director fd " + + "JOIN directors d ON fd.director_id = d.id " + + "WHERE fd.film_id = ? " + + "ORDER BY d.id"; + private static final String FIND_DIRECTORS_BY_FILMS_QUERY = + "SELECT fd.film_id, d.id, d.name " + + "FROM film_director fd " + + "JOIN directors d ON fd.director_id = d.id " + + "WHERE fd.film_id IN (%s) " + + "ORDER BY fd.film_id, d.id"; + private static final String FIND_DIRECTORS_BY_LIKES_AND_YEAR = + "SELECT f.id, f.name, f.description, f.release_date, f.duration, " + + "f.mpa_id, m.name AS mpa_name " + + "FROM films f " + + "JOIN film_director fd ON f.id = fd.film_id " + + "LEFT JOIN mpa m ON f.mpa_id = m.id " + + "LEFT JOIN film_likes fl ON f.id = fl.film_id " + + "WHERE fd.director_id = ? " + + "GROUP BY f.id, f.name, f.description, f.release_date, " + + "f.duration, f.mpa_id, m.name "; private final JdbcTemplate jdbc; private final FilmRowMapper mapper; private final GenreRowMapper genreMapper; + private final DirectorRowMapper directorMapper; @Autowired - public FilmDbStorage(JdbcTemplate jdbc, FilmRowMapper mapper, GenreRowMapper genreMapper) { + public FilmDbStorage(JdbcTemplate jdbc, FilmRowMapper mapper, GenreRowMapper genreMapper, DirectorRowMapper directorMapper) { this.jdbc = jdbc; this.mapper = mapper; this.genreMapper = genreMapper; + this.directorMapper = directorMapper; } @Override @@ -103,6 +132,7 @@ public Film add(Film film) { Long id = keyHolder.getKeyAs(Long.class); saveGenres(id, film.getGenres()); + saveDirector(id, film.getDirectors()); // Перечитываем из базы: так в ответе будут названия рейтинга и жанров, а не только их id return findById(id).orElseThrow(); } @@ -112,6 +142,7 @@ public Film update(Film film) { jdbc.update(UPDATE_QUERY, film.getName(), film.getDescription(), Date.valueOf(film.getReleaseDate()), film.getDuration(), getMpaId(film), film.getId()); saveGenres(film.getId(), film.getGenres()); + saveDirector(film.getId(), film.getDirectors()); return findById(film.getId()).orElseThrow(); } @@ -127,6 +158,7 @@ public Film delete(long id) { public Collection findAll() { List films = jdbc.query(FIND_ALL_QUERY, mapper); loadGenresForFilms(films); + loadDirectors(films); return films; } @@ -134,6 +166,7 @@ public Collection findAll() { public Optional findById(long id) { Optional film = jdbc.query(FIND_BY_ID_QUERY, mapper, id).stream().findFirst(); film.ifPresent(this::loadGenres); + film.ifPresent(this::loadDirectors); return film; } @@ -156,6 +189,7 @@ public List findPopular(int count, Integer genreId, Integer year) { String sql = buildPopularQuery(conditions) + LIMIT_CLAUSE; List films = jdbc.query(sql, mapper, params.toArray()); loadGenresForFilms(films); + loadDirectors(films); return films; } @@ -165,6 +199,7 @@ public List findCommon(long userId, long friendId) { String sql = buildPopularQuery(List.of(LIKED_BY_USER_FILTER, LIKED_BY_USER_FILTER)); List films = jdbc.query(sql, mapper, userId, friendId); loadGenresForFilms(films); + loadDirectors(films); return films; } @@ -179,7 +214,30 @@ public boolean removeLike(long filmId, long userId) { return jdbc.update(REMOVE_LIKE_QUERY, filmId, userId) > 0; } - // Собирает запрос: чтение + WHERE по условиям + сортировка по числу лайков, без LIMIT + @Override + public List findFilmByDirector(long directorId, String sortBy) { + String orderBy; + + if ("year".equals(sortBy)) { + orderBy = "ORDER BY f.release_date ASC, f.id"; + } else if ("likes".equals(sortBy)) { + orderBy = "ORDER BY COUNT(fl.user_id) DESC, f.id"; + } else { + throw new ValidationException( + "Неизвестный тип сортировки: " + sortBy + ); + } + + List films = jdbc.query(FIND_DIRECTORS_BY_LIKES_AND_YEAR + orderBy, mapper, directorId + ); + + loadDirectors(films); + loadGenresForFilms(films); + + return films; + } + + // Собирает запрос популярных фильмов: базовое чтение + WHERE по условиям + группировка с лимитом private String buildPopularQuery(List conditions) { StringBuilder sql = new StringBuilder(FIND_POPULAR_BASE_QUERY); if (!conditions.isEmpty()) { @@ -226,4 +284,47 @@ private void loadGenresForFilms(List films) { film.getGenres().add(genreMapper.mapRow(rs, rs.getRow())); }, filmsById.keySet().toArray()); } + + // Директора одного фильма + private void loadDirectors(Film film) { + film.getDirectors().addAll(jdbc.query(FIND_DIRECTORS_BY_FILM_QUERY, directorMapper, film.getId())); + } + + private void loadDirectors(List films) { + if (films.isEmpty()) { + return; // фильмов нет — запрос не нужен + } + Map filmsById = new HashMap<>(); + for (Film film : films) { + filmsById.put(film.getId(), film); + } + String placeholders = String.join( + ", ", + Collections.nCopies(filmsById.size(), "?") + ); + + String query = String.format( + FIND_DIRECTORS_BY_FILMS_QUERY, + placeholders + ); + + jdbc.query(query, rs -> { + Film film = filmsById.get(rs.getLong("film_id")); + film.getDirectors().add(directorMapper.mapRow(rs, rs.getRow()) + ); + }, filmsById.keySet().toArray()); + } + + // Заменяет директоров фильма: старые связи удаляются, новые вставляются одним пакетом + private void saveDirector(long filmId, Set directors) { + jdbc.update(DELETE_DIRECTOR_FILM_QUERY, filmId); + if (directors == null || directors.isEmpty()) { + return; + } + List rows = new ArrayList<>(); + for (Director director : directors) { + rows.add(new Object[]{filmId, director.getId()}); + } + jdbc.batchUpdate(INSERT_FILM_DIRECTOR_QUERY, rows); + } } \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmStorage.java index 3e5ecd3..62f4a02 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmStorage.java +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmStorage.java @@ -25,4 +25,6 @@ public interface FilmStorage { void addLike(long filmId, long userId); boolean removeLike(long filmId, long userId); + + List findFilmByDirector(long directorId, String sortBy); } \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/mapper/DirectorRowMapper.java b/src/main/java/ru/yandex/practicum/filmorate/storage/mapper/DirectorRowMapper.java new file mode 100644 index 0000000..c8e662f --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/mapper/DirectorRowMapper.java @@ -0,0 +1,19 @@ +package ru.yandex.practicum.filmorate.storage.mapper; + +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Component; +import ru.yandex.practicum.filmorate.model.Director; +import java.sql.ResultSet; +import java.sql.SQLException; + +@Component +public class DirectorRowMapper implements RowMapper { + + @Override + public Director mapRow(ResultSet rs, int rowNum) throws SQLException { + Director director = new Director(); + director.setId(rs.getLong("id")); + director.setName(rs.getString("name")); + return director; + } +} diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index 3d660c3..d1f73f2 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -52,4 +52,16 @@ CREATE TABLE IF NOT EXISTS friendship ( user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, friend_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, PRIMARY KEY (user_id, friend_id) +); + +CREATE TABLE IF NOT EXISTS directors ( + id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + name VARCHAR(255) NOT NULL +); + +--Связь «фильм — режиссёр», многие ко многим. +CREATE TABLE IF NOT EXISTS film_director ( + film_id BIGINT NOT NULL REFERENCES films(id) ON DELETE CASCADE, + director_id BIGINT NOT NULL REFERENCES directors(id) ON DELETE CASCADE, + PRIMARY KEY (film_id, director_id) ); \ No newline at end of file diff --git a/src/test/java/ru/yandex/practicum/filmorate/storage/director/DirectorDbStorageTest.java b/src/test/java/ru/yandex/practicum/filmorate/storage/director/DirectorDbStorageTest.java new file mode 100644 index 0000000..d00716c --- /dev/null +++ b/src/test/java/ru/yandex/practicum/filmorate/storage/director/DirectorDbStorageTest.java @@ -0,0 +1,75 @@ +package ru.yandex.practicum.filmorate.storage.director; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; +import org.springframework.context.annotation.Import; +import ru.yandex.practicum.filmorate.model.Director; +import ru.yandex.practicum.filmorate.storage.mapper.DirectorRowMapper; +import java.util.List; +import static org.junit.jupiter.api.Assertions.*; + +@JdbcTest +@AutoConfigureTestDatabase +@Import({DirectorDbStorage.class, DirectorRowMapper.class}) +public class DirectorDbStorageTest { + + private final DirectorDbStorage directorDbStorage; + + @Autowired + public DirectorDbStorageTest(DirectorDbStorage directorDbStorage) { + this.directorDbStorage = directorDbStorage; + } + + private Director createDirector(String directorName) { + Director director = new Director(); + director.setName(directorName); + return director; + } + + @Test + public void addDirector() { + Director director = createDirector("name"); + directorDbStorage.addDirector(director); + assertEquals("name", director.getName()); + assertNotNull(director.getId()); + } + + @Test + public void getDirectorById() { + Director director = createDirector("director"); + Director createdDirector = directorDbStorage.addDirector(director); + Director retrievedDirector = directorDbStorage.findDirectorById(createdDirector.getId()).orElse(null); + assertEquals(createdDirector.getId(), retrievedDirector.getId()); + assertEquals(createdDirector.getName(), retrievedDirector.getName()); + } + + @Test + public void deleteDirectorById() { + Director director = createDirector("director"); + directorDbStorage.addDirector(director); + directorDbStorage.removeDirector(director.getId()); + assertTrue(directorDbStorage.findDirectorById(director.getId()).isEmpty()); + } + + @Test + public void updateDirectorById() { + Director director = createDirector("director"); + directorDbStorage.addDirector(director); + director.setName("updated name"); + directorDbStorage.updateDirector(director); + Director updatedDirector = directorDbStorage.findDirectorById(director.getId()).orElse(null); + assertEquals("updated name", updatedDirector.getName()); + } + + @Test + public void getAllDirectors() { + Director director = createDirector("director"); + Director director2 = createDirector("director2"); + directorDbStorage.addDirector(director); + directorDbStorage.addDirector(director2); + List allDirectors = directorDbStorage.findAllDirectors(); + assertEquals(2, allDirectors.size()); + } +} diff --git a/src/test/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorageTest.java b/src/test/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorageTest.java index 67cb17b..275a169 100644 --- a/src/test/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorageTest.java +++ b/src/test/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorageTest.java @@ -5,10 +5,9 @@ import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; import org.springframework.context.annotation.Import; -import ru.yandex.practicum.filmorate.model.Film; -import ru.yandex.practicum.filmorate.model.Genre; -import ru.yandex.practicum.filmorate.model.Mpa; -import ru.yandex.practicum.filmorate.model.User; +import ru.yandex.practicum.filmorate.model.*; +import ru.yandex.practicum.filmorate.storage.director.DirectorDbStorage; +import ru.yandex.practicum.filmorate.storage.mapper.DirectorRowMapper; import ru.yandex.practicum.filmorate.storage.mapper.FilmRowMapper; import ru.yandex.practicum.filmorate.storage.mapper.GenreRowMapper; import ru.yandex.practicum.filmorate.storage.mapper.UserRowMapper; @@ -23,16 +22,19 @@ @JdbcTest @AutoConfigureTestDatabase @Import({FilmDbStorage.class, FilmRowMapper.class, GenreRowMapper.class, - UserDbStorage.class, UserRowMapper.class}) + UserDbStorage.class, UserRowMapper.class, + DirectorDbStorage.class, DirectorRowMapper.class}) class FilmDbStorageTest { private final FilmDbStorage filmStorage; private final UserDbStorage userStorage; + private final DirectorDbStorage directorStorage; @Autowired - FilmDbStorageTest(FilmDbStorage filmStorage, UserDbStorage userStorage) { + FilmDbStorageTest(FilmDbStorage filmStorage, UserDbStorage userStorage, DirectorDbStorage directorStorage) { this.filmStorage = filmStorage; this.userStorage = userStorage; + this.directorStorage = directorStorage; } // Фильм без рейтинга и жанров: тесты добавляют их сами, где нужно @@ -74,6 +76,12 @@ private static List genreNames(Film film) { return film.getGenres().stream().map(Genre::getName).toList(); } + private Director createDirector(String directorName) { + Director director = new Director(); + director.setName(directorName); + return director; + } + @Test void shouldAddFilmWithMpaAndGenres() { Film film = newFilm("С жанрами"); @@ -256,4 +264,48 @@ void shouldReturnEmptyCommonFilmsWhenNoSharedLikes() { assertEquals(0, filmStorage.findCommon(anna.getId(), boris.getId()).size()); } + + @Test + void shouldReturnAllFilmsByDirectorSortedByLikes() { + Director director = createDirector("Christopher Nolan"); + directorStorage.addDirector(director); + Film film1 = newFilm("Фильм 1"); + film1.getDirectors().add(director); + film1 = filmStorage.add(film1); + Film film2 = newFilm("Фильм 2"); + film2.getDirectors().add(director); + film2 = filmStorage.add(film2); + User anna = createUser("anna"); + User boris = createUser("boris"); + filmStorage.addLike(film1.getId(), anna.getId()); + filmStorage.addLike(film2.getId(), anna.getId()); + filmStorage.addLike(film2.getId(), boris.getId()); + + List allFilms = filmStorage.findFilmByDirector(director.getId(), "likes"); + assertEquals(2, allFilms.size()); + assertEquals(film2.getId(), allFilms.get(0).getId()); + assertEquals(film1.getId(), allFilms.get(1).getId()); + } + + @Test + void shouldReturnAllFilmsByDirectorSortedByYear() { + Director director = createDirector("Steven Spielberg"); + directorStorage.addDirector(director); + Film film1 = newFilm("Фильм 1"); + film1.setReleaseDate(LocalDate.of(2000, 1, 1)); + film1.getDirectors().add(director); + film1 = filmStorage.add(film1); + Film film2 = newFilm("Фильм 2"); + film2.setReleaseDate(LocalDate.of(2010, 1, 1)); + film2.getDirectors().add(director); + film2 = filmStorage.add(film2); + Film film3 = newFilm("Фильм 3"); + film3.setReleaseDate(LocalDate.of(2005, 1, 1)); + film3.getDirectors().add(director); + film3 = filmStorage.add(film3); + List allFilms = filmStorage.findFilmByDirector(director.getId(), "year"); + assertEquals(film1.getId(), allFilms.get(0).getId()); + assertEquals(film3.getId(), allFilms.get(1).getId()); + assertEquals(film2.getId(), allFilms.get(2).getId()); + } } \ No newline at end of file From 6a6f5bad55fc68d75c53d899ea9322dcc3d2aa55 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 8 Oct 2026 17:09:46 +0300 Subject: [PATCH 2/6] trigger ci From 36620a7b77bd644f14c51c9ae9718cefa4955c24 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 8 Oct 2026 17:58:47 +0300 Subject: [PATCH 3/6] Fixed mistakes --- .../filmorate/controller/FilmController.java | 2 -- .../ru/yandex/practicum/filmorate/model/Film.java | 1 - .../practicum/filmorate/service/FilmService.java | 1 - .../storage/director/DirectorDbStorage.java | 12 +++++++++++- .../filmorate/storage/film/FilmStorage.java | 1 - .../filmorate/storage/film/FilmDbStorageTest.java | 2 -- 6 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java index 2085d18..ee7c157 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java @@ -6,7 +6,6 @@ import org.springframework.web.bind.annotation.*; import ru.yandex.practicum.filmorate.model.Film; import ru.yandex.practicum.filmorate.service.FilmService; - import java.util.Collection; @RestController @@ -77,6 +76,5 @@ public Collection getFilmsByDirector(@PathVariable long directorId, @RequestParam (defaultValue = "likes") String sortBy) { log.info("GET /films/director/{}?sortBy={}", directorId, sortBy); return filmService.findByDirectorSorted(directorId, sortBy); - } } \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/Film.java b/src/main/java/ru/yandex/practicum/filmorate/model/Film.java index 795626f..931622f 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/model/Film.java +++ b/src/main/java/ru/yandex/practicum/filmorate/model/Film.java @@ -6,7 +6,6 @@ import jakarta.validation.constraints.Positive; import jakarta.validation.constraints.Size; import lombok.Data; - import java.time.LocalDate; import java.util.HashSet; import java.util.LinkedHashSet; diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java b/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java index 37b8b50..cd07963 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java +++ b/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java @@ -10,7 +10,6 @@ import ru.yandex.practicum.filmorate.model.Genre; import ru.yandex.practicum.filmorate.storage.film.FilmStorage; import ru.yandex.practicum.filmorate.storage.user.UserStorage; - import java.time.LocalDate; import java.util.Collection; import java.util.HashSet; diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/director/DirectorDbStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/director/DirectorDbStorage.java index fa4eb7c..4e1752d 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/storage/director/DirectorDbStorage.java +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/director/DirectorDbStorage.java @@ -43,7 +43,17 @@ public Director addDirector(Director director) { @Override public Director updateDirector(Director director) { - jdbcTemplate.update(UPDATE_QUERY, director.getName(), director.getId()); + int updatedRows = jdbcTemplate.update( + UPDATE_QUERY, + director.getName(), + director.getId() + ); + + if (updatedRows == 0) { + throw new NotFoundException( + "Режиссёр с id=" + director.getId() + " не найден" + ); + } return director; } diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmStorage.java index 62f4a02..16417e2 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmStorage.java +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmStorage.java @@ -1,7 +1,6 @@ package ru.yandex.practicum.filmorate.storage.film; import ru.yandex.practicum.filmorate.model.Film; - import java.util.Collection; import java.util.List; import java.util.Optional; diff --git a/src/test/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorageTest.java b/src/test/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorageTest.java index 275a169..cb31c13 100644 --- a/src/test/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorageTest.java +++ b/src/test/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorageTest.java @@ -12,10 +12,8 @@ import ru.yandex.practicum.filmorate.storage.mapper.GenreRowMapper; import ru.yandex.practicum.filmorate.storage.mapper.UserRowMapper; import ru.yandex.practicum.filmorate.storage.user.UserDbStorage; - import java.time.LocalDate; import java.util.List; - import static org.junit.jupiter.api.Assertions.*; // Лайкам нужны настоящие пользователи, поэтому подключаем и их хранилище From 3947d82c4a10fb823ee6d2088c1a41be28203ea0 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 8 Oct 2026 18:25:29 +0300 Subject: [PATCH 4/6] Fixed small mistakes --- .../practicum/filmorate/controller/DirectorController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/DirectorController.java b/src/main/java/ru/yandex/practicum/filmorate/controller/DirectorController.java index 2747dbe..e0a4785 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/DirectorController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/DirectorController.java @@ -33,13 +33,13 @@ public Director getDirectorById(@PathVariable Long id) { @PostMapping public Director createDirector(@RequestBody Director director) { - log.info("POST /directors/{}", director); + log.info("POST /directors, body:{}", director); return directorService.addDirector(director); } @PutMapping public Director updateDirector(@RequestBody Director director) { - log.info("PUT /directors/{}", director); + log.info("PUT /directors, body:{}", director); return directorService.updateDirector(director); } From bb65ce40b7bd8270cdd6f1cc7f33e41fb151e646 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 8 Oct 2026 22:50:19 +0300 Subject: [PATCH 5/6] Fixed mistakes and added info in README --- README.md | 25 ++++++++++++++++- .../controller/DirectorController.java | 5 ++-- .../filmorate/controller/FilmController.java | 1 + .../practicum/filmorate/model/Director.java | 4 ++- .../practicum/filmorate/model/Film.java | 4 ++- .../filmorate/service/DirectorService.java | 2 +- .../filmorate/service/FilmService.java | 27 ++++++++++++++++++- .../storage/director/DirectorDbStorage.java | 4 +-- .../filmorate/storage/film/FilmDbStorage.java | 3 ++- .../filmorate/storage/film/FilmStorage.java | 1 + .../storage/film/FilmDbStorageTest.java | 8 +++++- 11 files changed, 73 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 50b4159..16968f0 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,8 @@ - Хранение данных в базе H2 (файловый режим — данные переживают перезапуск), доступ через `JdbcTemplate` и `RowMapper`. - Справочники жанров и рейтингов MPA в базе, эндпоинты `/genres` и `/mpa`. -- У фильма есть рейтинг и список жанров; жанры в ответе — без дублей, по порядку id. +- У фильма есть рейтинг и список жанров; жанры в ответе — без дублей, по порядку id. +- У фильмов можно указывать режиссёров и получать список фильмов выбранного режиссёра с сортировкой по году выпуска или количеству лайков. - Односторонняя дружба: добавление/удаление друзей, список друзей, общие друзья. - Лайки фильмам: добавление/удаление, топ-N фильмов по количеству лайков. - Слоистая архитектура: контроллер → сервис → хранилище, выбор реализации хранилища через `@Qualifier`. @@ -70,6 +71,9 @@ - `PUT /films/{id}/like/{userId}` — поставить лайк фильму. - `DELETE /films/{id}/like/{userId}` — убрать лайк. - `GET /films/popular?count={count}` — топ-N фильмов по лайкам (по умолчанию 10). +- `GET /films/director/{directorId}` — получить список фильмов режиссёра. +- `GET /films/director/{directorId}?sortBy=likes` — получить список фильмов режиссёра, отсортированный по лайкам. +- `GET /films/director/{directorId}?sortBy=year` — получить список фильмов режиссёра, отсортированный по году выпуска. ### Пользователи (`/users`) - `GET /users` — получить список всех пользователей. @@ -89,6 +93,13 @@ - `GET /mpa` — получить список всех рейтингов. - `GET /mpa/{id}` — получить рейтинг по ID. +## Режиссёры (`/directors`) +- `GET /directors` — получить список всех режиссёров. +- `GET /directors/{id}` — получить режиссёра по ID. +- `POST /directors` — создать нового режиссёра. +- `PUT /directors` — обновить данные режиссёра. +- `DELETE /directors/{id}` — удалить режиссёра. + ## Схема базы данных ```mermaid @@ -100,6 +111,8 @@ erDiagram users ||--o{ film_likes : "user_id" users ||--o{ friendship : "user_id" users ||--o{ friendship : "friend_id" + films ||--o{film_director : "film_id" + directors ||--o{film_director : "director_id" films { BIGINT id PK @@ -136,6 +149,14 @@ erDiagram BIGINT user_id PK, FK BIGINT friend_id PK, FK } + directors { + BIGINT id PK + VARCHAR name + } + film_director { + BIGINT film_id PK, FK + BIGINT director_id PK, FK + } ``` | Таблица | Что хранит | @@ -147,6 +168,8 @@ erDiagram | `film_genres` | связь «фильм — жанр», многие ко многим | | `film_likes` | лайки: какой пользователь лайкнул какой фильм | | `friendship` | дружба: одна строка — `user_id` добавил в друзья `friend_id`; подтверждённая (взаимная) дружба — две встречные строки | +| `directors` | режиссёры | +| `film_director` | связь "фильм — режиссёр", многие ко многим | У таблиц-связок составной первичный ключ, поэтому одна и та же пара не запишется дважды. При удалении фильма или пользователя связанные с ним записи удаляются каскадно. diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/DirectorController.java b/src/main/java/ru/yandex/practicum/filmorate/controller/DirectorController.java index e0a4785..d9baa58 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/DirectorController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/DirectorController.java @@ -1,5 +1,6 @@ package ru.yandex.practicum.filmorate.controller; +import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @@ -32,13 +33,13 @@ public Director getDirectorById(@PathVariable Long id) { } @PostMapping - public Director createDirector(@RequestBody Director director) { + public Director createDirector(@Valid @RequestBody Director director) { log.info("POST /directors, body:{}", director); return directorService.addDirector(director); } @PutMapping - public Director updateDirector(@RequestBody Director director) { + public Director updateDirector(@Valid @RequestBody Director director) { log.info("PUT /directors, body:{}", director); return directorService.updateDirector(director); } diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java index ee7c157..9e85b2c 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java @@ -6,6 +6,7 @@ import org.springframework.web.bind.annotation.*; import ru.yandex.practicum.filmorate.model.Film; import ru.yandex.practicum.filmorate.service.FilmService; + import java.util.Collection; @RestController diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/Director.java b/src/main/java/ru/yandex/practicum/filmorate/model/Director.java index ed2dad9..5924c72 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/model/Director.java +++ b/src/main/java/ru/yandex/practicum/filmorate/model/Director.java @@ -1,10 +1,12 @@ package ru.yandex.practicum.filmorate.model; +import jakarta.validation.constraints.NotBlank; import lombok.Data; @Data public class Director { - private Long id; + + @NotBlank(message = "Имя режиссера не может быть пустым") private String name; } diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/Film.java b/src/main/java/ru/yandex/practicum/filmorate/model/Film.java index 931622f..5ba8b59 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/model/Film.java +++ b/src/main/java/ru/yandex/practicum/filmorate/model/Film.java @@ -6,6 +6,7 @@ import jakarta.validation.constraints.Positive; import jakarta.validation.constraints.Size; import lombok.Data; + import java.time.LocalDate; import java.util.HashSet; import java.util.LinkedHashSet; @@ -37,6 +38,8 @@ public class Film { @JsonIgnore private Set likes = new HashSet<>(); + private Set directors = new LinkedHashSet<>(); + public void addLike(Long userId) { likes.add(userId); } @@ -45,5 +48,4 @@ public boolean removeLike(Long userId) { return likes.remove(userId); } - private Set directors = new LinkedHashSet<>(); } diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/DirectorService.java b/src/main/java/ru/yandex/practicum/filmorate/service/DirectorService.java index c028138..df8c8a6 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/service/DirectorService.java +++ b/src/main/java/ru/yandex/practicum/filmorate/service/DirectorService.java @@ -29,7 +29,7 @@ public void deleteDirector(long id) { public Director getDirectorById(long id) { return directorStorage.findDirectorById(id) - .orElseThrow(() -> new NotFoundException("Director with id " + id + " not found")); + .orElseThrow(() -> new NotFoundException("Режиссер с id " + id + " не найден")); } public java.util.List getAllDirectors() { diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java b/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java index cd07963..7d1da36 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java +++ b/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java @@ -6,10 +6,12 @@ import org.springframework.stereotype.Service; import ru.yandex.practicum.filmorate.exception.NotFoundException; import ru.yandex.practicum.filmorate.exception.ValidationException; +import ru.yandex.practicum.filmorate.model.Director; import ru.yandex.practicum.filmorate.model.Film; import ru.yandex.practicum.filmorate.model.Genre; import ru.yandex.practicum.filmorate.storage.film.FilmStorage; import ru.yandex.practicum.filmorate.storage.user.UserStorage; + import java.time.LocalDate; import java.util.Collection; import java.util.HashSet; @@ -29,16 +31,19 @@ public class FilmService { // Нужны для проверки рейтинга и жанра из запроса private final MpaService mpaService; private final GenreService genreService; + private final DirectorService directorService; @Autowired public FilmService(@Qualifier("filmDbStorage") FilmStorage filmStorage, @Qualifier("userDbStorage") UserStorage userStorage, MpaService mpaService, - GenreService genreService) { + GenreService genreService, + DirectorService directorService) { this.filmStorage = filmStorage; this.userStorage = userStorage; this.mpaService = mpaService; this.genreService = genreService; + this.directorService = directorService; } // Получение фильма по id с проверкой существования @@ -133,9 +138,27 @@ private void validateMpaAndGenres(Film film) { } } + private void validateDirectors(Film film) { + + if (film.getDirectors() == null || film.getDirectors().isEmpty()) { + return; + } + + Set knownIds = new HashSet<>(); + for (Director director : directorService.getAllDirectors()) { + knownIds.add(director.getId()); + } + for (Director director : film.getDirectors()) { + if (!knownIds.contains(director.getId())) { + throw new NotFoundException("Режиссёр с id=" + director.getId() + " не найден"); + } + } + } + public Film add(Film film) { validateReleaseDate(film); validateMpaAndGenres(film); + validateDirectors(film); return filmStorage.add(film); } @@ -143,6 +166,7 @@ public Film update(Film film) { getFilmOrThrow(film.getId()); // проверка существования — иначе 404 validateReleaseDate(film); validateMpaAndGenres(film); + validateDirectors(film); return filmStorage.update(film); } @@ -155,6 +179,7 @@ public Film findById(long id) { } public Collection findByDirectorSorted(long directorId, String sortBy) { + directorService.getDirectorById(directorId); // проверка существования режиссёра — иначе 404 return filmStorage.findFilmByDirector(directorId, sortBy); } } \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/director/DirectorDbStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/director/DirectorDbStorage.java index 4e1752d..c4bb187 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/storage/director/DirectorDbStorage.java +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/director/DirectorDbStorage.java @@ -21,7 +21,7 @@ public class DirectorDbStorage implements DirectorStorage { private static final String UPDATE_QUERY = "UPDATE directors SET name = ? WHERE id = ?"; private static final String DELETE_QUERY = "DELETE FROM directors WHERE id = ?"; private static final String SELECT_BY_ID_QUERY = "SELECT * FROM directors WHERE id = ?"; - private static final String SELECT_ALL_QUERY = "SELECT * FROM directors"; + private static final String SELECT_ALL_QUERY = "SELECT * FROM directors ORDER BY id"; @Autowired public DirectorDbStorage(JdbcTemplate jdbcTemplate, DirectorRowMapper directorRowMapper) { @@ -60,7 +60,7 @@ public Director updateDirector(Director director) { @Override public void removeDirector(long id) { Director director = findDirectorById(id) - .orElseThrow(() -> new NotFoundException("Director not found with id: " + id)); + .orElseThrow(() -> new NotFoundException("Режиссёр с id " + id + " не найден")); jdbcTemplate.update(DELETE_QUERY, id); } diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorage.java index 8e898c3..787bd86 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorage.java +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorage.java @@ -13,6 +13,7 @@ import ru.yandex.practicum.filmorate.storage.mapper.DirectorRowMapper; import ru.yandex.practicum.filmorate.storage.mapper.FilmRowMapper; import ru.yandex.practicum.filmorate.storage.mapper.GenreRowMapper; + import java.sql.Date; import java.sql.PreparedStatement; import java.sql.Statement; @@ -237,7 +238,7 @@ public List findFilmByDirector(long directorId, String sortBy) { return films; } - // Собирает запрос популярных фильмов: базовое чтение + WHERE по условиям + группировка с лимитом + //Собирает запрос: чтение + WHERE по условиям + сортировка по числу лайков, без LIMIT private String buildPopularQuery(List conditions) { StringBuilder sql = new StringBuilder(FIND_POPULAR_BASE_QUERY); if (!conditions.isEmpty()) { diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmStorage.java index 16417e2..62f4a02 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmStorage.java +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmStorage.java @@ -1,6 +1,7 @@ package ru.yandex.practicum.filmorate.storage.film; import ru.yandex.practicum.filmorate.model.Film; + import java.util.Collection; import java.util.List; import java.util.Optional; diff --git a/src/test/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorageTest.java b/src/test/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorageTest.java index cb31c13..fc59fc0 100644 --- a/src/test/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorageTest.java +++ b/src/test/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorageTest.java @@ -5,15 +5,21 @@ import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; import org.springframework.context.annotation.Import; -import ru.yandex.practicum.filmorate.model.*; +import ru.yandex.practicum.filmorate.model.Film; +import ru.yandex.practicum.filmorate.model.Director; +import ru.yandex.practicum.filmorate.model.Genre; +import ru.yandex.practicum.filmorate.model.Mpa; +import ru.yandex.practicum.filmorate.model.User; import ru.yandex.practicum.filmorate.storage.director.DirectorDbStorage; import ru.yandex.practicum.filmorate.storage.mapper.DirectorRowMapper; import ru.yandex.practicum.filmorate.storage.mapper.FilmRowMapper; import ru.yandex.practicum.filmorate.storage.mapper.GenreRowMapper; import ru.yandex.practicum.filmorate.storage.mapper.UserRowMapper; import ru.yandex.practicum.filmorate.storage.user.UserDbStorage; + import java.time.LocalDate; import java.util.List; + import static org.junit.jupiter.api.Assertions.*; // Лайкам нужны настоящие пользователи, поэтому подключаем и их хранилище From d834a5742d1e2c034cf35998aa2cb00495514898 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 9 Oct 2026 12:11:17 +0300 Subject: [PATCH 6/6] Added import --- .../filmorate/storage/review/ReviewDbStorageTest.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/test/java/ru/yandex/practicum/filmorate/storage/review/ReviewDbStorageTest.java b/src/test/java/ru/yandex/practicum/filmorate/storage/review/ReviewDbStorageTest.java index 95d380c..28bace8 100644 --- a/src/test/java/ru/yandex/practicum/filmorate/storage/review/ReviewDbStorageTest.java +++ b/src/test/java/ru/yandex/practicum/filmorate/storage/review/ReviewDbStorageTest.java @@ -10,10 +10,7 @@ import ru.yandex.practicum.filmorate.model.Review; import ru.yandex.practicum.filmorate.model.User; import ru.yandex.practicum.filmorate.storage.film.FilmDbStorage; -import ru.yandex.practicum.filmorate.storage.mapper.FilmRowMapper; -import ru.yandex.practicum.filmorate.storage.mapper.GenreRowMapper; -import ru.yandex.practicum.filmorate.storage.mapper.ReviewRowMapper; -import ru.yandex.practicum.filmorate.storage.mapper.UserRowMapper; +import ru.yandex.practicum.filmorate.storage.mapper.*; import ru.yandex.practicum.filmorate.storage.user.UserDbStorage; import java.time.LocalDate; @@ -24,7 +21,7 @@ @JdbcTest @AutoConfigureTestDatabase @Import({ReviewDbStorage.class, ReviewRowMapper.class, FilmDbStorage.class, FilmRowMapper.class, - GenreRowMapper.class, UserDbStorage.class, UserRowMapper.class}) + GenreRowMapper.class, UserDbStorage.class, UserRowMapper.class, DirectorRowMapper.class}) class ReviewDbStorageTest { private final ReviewDbStorage reviewStorage;