diff --git a/ER Diagramm.png b/ER Diagramm.png
new file mode 100644
index 0000000..360a74b
Binary files /dev/null and b/ER Diagramm.png differ
diff --git a/README.md b/README.md
index 2cf454a..94ba408 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,42 @@
-# java-filmorate
-Template repository for Filmorate project.
+# Java filmorate
+
+*Как работает база данных:*
+
+**1. Таблица movie**
+PK: movie_id (уникальный идентификатор фильма)
+Содержит основные поля с названием, описанием фильма, id жанра (one to one с таблицей genre), mpa_id(one to one с таблицей mpa)
+**2. Таблица movie_advanced**
+PK: movie_id (one to one с таблицей movie)
+Содержит дополнительную информацию о фильме. Создана для опциональных характеристик картины с запасом на расширение, что бы не перегружать остальное.
+**3. Таблицы genre, mpa** - содержат информацию о жанре и рейтинге фильма. Вынесены в отдельную таблицу для упрощения корректировок значений.
+**4. Таблица user**
+PK: user_id (уникальный идентификатор пользователя)
+Содержит основные поля, характеризующие пользователя
+**5. Таблица user_friendlist**
+PK: user_id+friend_id - ключ составной, связка id юзера+ id юзера-друга уникальна.
+Предполагается выгрузка списка друзей по user_id и фильтрация по friendship_status, который отражает статус взаимоотношений:
+0 - запрос отправлен от "друга"
+1 - запрос согласован.
+**6. Таблица movie_rating**
+PK: movie_id + user_id - ключ составной, связка фильма и id лайкнувшего юзера уникальна.
+Из неё можно будет каунтить рейтинг фильма по id, а также искать пересечения по лайкнувшим юзерам для составления списка обших фильмов.
+
+**Примеры запросов**
+1. Получение фильма по id:
+ SELECT*
+ FROM movie
+ WHERE movie_id = {id фильма};
+2. Получение TOP-10 фильмов
+ SELECT *
+ FROM movie
+ WHERE movie_id IN (SELECT movie_id
+ FROM movie_rating
+ GROUP BY movie_id
+ ORDER BY COUNT(user_id) DESC
+ LIMIT 10);
+
+
+
+
+
+SP12 start
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 7d653f4..55240c4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -51,6 +51,24 @@
spring-boot-starter-test
test
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ com.h2database
+ h2
+ 2.1.210
+ runtime
+
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+
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 8f3cccf..b4662c8 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java
@@ -1,7 +1,6 @@
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.Film;
@@ -50,4 +49,9 @@ public Film removeLike(@PathVariable Integer filmId, @PathVariable Integer id) {
public Collection getTop(@RequestParam(defaultValue = "10") Integer count) {
return filmService.top(count);
}
+
+ @GetMapping("/{id}")
+ public Film getFilm(@PathVariable Integer id) {
+ return filmService.getFilm(id);
+ }
}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/GenreController.java b/src/main/java/ru/yandex/practicum/filmorate/controller/GenreController.java
new file mode 100644
index 0000000..8faf1ff
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/controller/GenreController.java
@@ -0,0 +1,32 @@
+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.Genre;
+import ru.yandex.practicum.filmorate.service.FilmService;
+
+import java.util.Collection;
+
+@Slf4j
+@RestController
+@RequestMapping("/genres")
+public class GenreController {
+
+ private final FilmService filmService;
+
+ @Autowired
+ public GenreController(FilmService filmService) {
+ this.filmService = filmService;
+ }
+
+ @GetMapping
+ public Collection getAllGenres() {
+ return filmService.getAllGenres();
+ }
+
+ @GetMapping("/{id}")
+ public Genre getGenre(@PathVariable Integer id) {
+ return filmService.getGenre(id);
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/MPAController.java b/src/main/java/ru/yandex/practicum/filmorate/controller/MPAController.java
new file mode 100644
index 0000000..b6c1ac0
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/controller/MPAController.java
@@ -0,0 +1,35 @@
+package ru.yandex.practicum.filmorate.controller;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import ru.yandex.practicum.filmorate.model.Mpa;
+import ru.yandex.practicum.filmorate.service.FilmService;
+
+import java.util.Collection;
+
+@Slf4j
+@RestController
+@RequestMapping("/mpa")
+public class MPAController {
+
+ private final FilmService filmService;
+
+ @Autowired
+ public MPAController(FilmService filmService) {
+ this.filmService = filmService;
+ }
+
+ @GetMapping
+ public Collection getAllGenres() {
+ return filmService.getAllMpa();
+ }
+
+ @GetMapping("/{id}")
+ public Mpa getGenre(@PathVariable Integer id) {
+ return filmService.getMpa(id);
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java
index 792ef82..cd33d50 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java
@@ -57,4 +57,9 @@ public Collection getFriends(@PathVariable Integer id) {
return userService.getFriends(id);
}
+ @GetMapping("/test")
+ public Collection testSequense(@PathVariable Integer id) {
+ return userService.getFriends(id);
+ }
+
}
\ 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 fe52d60..477acf8 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/model/Film.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/model/Film.java
@@ -3,14 +3,16 @@
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Data;
+import lombok.NoArgsConstructor;
import ru.yandex.practicum.filmorate.exceptions.UnknownDataException;
import java.time.LocalDate;
+import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
-
@AllArgsConstructor
+@NoArgsConstructor
@Data
public class Film {
private int id;
@@ -19,6 +21,8 @@ public class Film {
private LocalDate releaseDate;
private long duration;
private int rating = 0;
+ private Collection genres;
+ private Mpa mpa;
@JsonIgnore
private Set whoLikes;
@@ -48,3 +52,4 @@ public Film removeLike(Integer id) {
}
+
diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/Genre.java b/src/main/java/ru/yandex/practicum/filmorate/model/Genre.java
new file mode 100644
index 0000000..7f7eaf4
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/model/Genre.java
@@ -0,0 +1,24 @@
+package ru.yandex.practicum.filmorate.model;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode
+public class Genre {
+ private int id;
+ private String name;
+
+ public Genre(int id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/Mpa.java b/src/main/java/ru/yandex/practicum/filmorate/model/Mpa.java
new file mode 100644
index 0000000..ebccd05
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/model/Mpa.java
@@ -0,0 +1,22 @@
+package ru.yandex.practicum.filmorate.model;
+
+import lombok.Data;
+
+@Data
+public class Mpa {
+ private int id;
+ private String name;
+
+ public Mpa(int id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/User.java b/src/main/java/ru/yandex/practicum/filmorate/model/User.java
index 662b7af..f99fbfe 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/model/User.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/model/User.java
@@ -2,6 +2,7 @@
import lombok.AllArgsConstructor;
import lombok.Data;
+import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.time.LocalDate;
@@ -11,6 +12,7 @@
@Slf4j
@AllArgsConstructor
+@NoArgsConstructor
@Data
public class User {
private int id;
@@ -18,26 +20,9 @@ public class User {
private String login;
private String name;
private LocalDate birthday;
- private Set friends;
+ private Set friends;
- public User addFriend(Integer friendId) {
- if (friends == null) {
- friends = new HashSet<>();
- }
- friends.add(friendId);
- log.info("Друзья пользователя " + id + ": " + friends);
- return this;
- }
-
- public User deleteFriend(Integer friendId) {
- if (friends == null) {
- friends = new HashSet<>();
- }
- friends.remove(friendId);
- return this;
- }
-
- public Collection getFriends() {
+ public Collection getFriends() {
if (friends == null) {
friends = new HashSet<>();
}
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 275d547..6f18962 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java
@@ -3,77 +3,78 @@
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
-import ru.yandex.practicum.filmorate.exceptions.UnknownDataException;
import ru.yandex.practicum.filmorate.model.Film;
-import ru.yandex.practicum.filmorate.storage.film.InMemoryFilmStorage;
-import ru.yandex.practicum.filmorate.storage.user.InMemoryUserStorage;
+import ru.yandex.practicum.filmorate.model.Genre;
+import ru.yandex.practicum.filmorate.model.Mpa;
+import ru.yandex.practicum.filmorate.storage.film.FilmDbStorage;
+import ru.yandex.practicum.filmorate.storage.user.UserStorage;
import java.util.Collection;
-import java.util.Comparator;
-import java.util.stream.Collectors;
@Service
@Slf4j
public class FilmService {
- private final InMemoryFilmStorage inMemoryFilmStorage;
- private final InMemoryUserStorage inMemoryUserStorage;
+ private final FilmDbStorage filmStorage;
@Autowired
- public FilmService(InMemoryFilmStorage inMemoryFilmStorage, InMemoryUserStorage inMemoryUserStorage) {
- this.inMemoryFilmStorage = inMemoryFilmStorage;
- this.inMemoryUserStorage = inMemoryUserStorage;
+ public FilmService(FilmDbStorage filmStorage, UserStorage userStorage) {
+ this.filmStorage = filmStorage;
}
public Collection findAllFilms() {
- return inMemoryFilmStorage.findAllFilms();
+ log.info("Команда: найти все фильмы");
+ return filmStorage.findAllFilms();
}
public Film createFilm(Film film) {
- return inMemoryFilmStorage.createFilm(film);
+ log.info("Команда: создать фильм " + film.getName());
+ return filmStorage.createFilm(film);
}
public Film updateFilm(Film film) {
- return inMemoryFilmStorage.updateFilm(film);
+ log.info("Команда: обновить фильм " + film.getName());
+ return filmStorage.updateFilm(film);
}
- public Film addLike(Integer filmId, Integer id) {
- if (!checkFilmUserAvalaibility(filmId, id)) {
- throw new UnknownDataException("Запрошенные ресурсы отсутствуют. Невозможно поставить лайк.");
- }
- log.info("Запрошено добавление лайка фильму: " + filmId + ". От пользователя: " + id);
- return inMemoryFilmStorage.findAllFilms().stream()
- .filter(film -> film.getId() == filmId)
- .map(film -> film.addLike(id))
- .findFirst()
- .orElseThrow(() -> new UnknownDataException("Запрошенные ресурсы отсутствуют"));
- }
public Film removeLike(Integer filmId, Integer id) {
- if (!checkFilmUserAvalaibility(filmId, id)) {
- throw new UnknownDataException("Запрошенные ресурсы отсутствуют. Невозможно удалить лайк.");
- }
- log.info("Запрошено удаление лайка фильму: " + filmId + ". От пользователя: " + id);
- return inMemoryFilmStorage.findAllFilms().stream()
- .filter(film -> film.getId() == filmId)
- .map(film -> film.removeLike(id))
- .findFirst()
- .orElseThrow(() -> new UnknownDataException("Запрошенные ресурсы отсутствуют"));
+ log.info("Команда: удалить лайк");
+ return filmStorage.removeLike(filmId, id);
}
public Collection top(Integer count) {
- log.info("Запрошен топ фильмов в количестве: " + count);
- return inMemoryFilmStorage.findAllFilms().stream()
- .sorted(Comparator.comparingInt(Film::getRating).reversed())
- .limit(count)
- .collect(Collectors.toList());
+ log.info("Команда: получить топ фильмов (" + count + ")");
+ return filmStorage.top(count);
+ }
+
+ public Film addLike(Integer filmId, Integer id) {
+ log.info("Команда: добавить лайк");
+ return filmStorage.addLike(filmId, id);
+ }
+
+ public Collection getAllGenres() {
+ log.info("Команда: получить все жанры");
+ return filmStorage.getAllGenres();
+ }
+
+ public Genre getGenre(Integer count) {
+ log.info("Команда: получить жанр");
+ return filmStorage.getGenre(count);
+ }
+
+ public Collection getAllMpa() {
+ log.info("Команда: получить все возрастные рейтинги");
+ return filmStorage.getAllMpa();
+ }
+ public Mpa getMpa(Integer id) {
+ log.info("Команда: получить возрастной рейтинг");
+ return filmStorage.getMpa(id);
}
- //проверка наличия
- //проверка добавления. Если всё хорошо, вернёт true
- private boolean checkFilmUserAvalaibility(Integer filmId, Integer id) {
- return inMemoryUserStorage.findAllUsers().stream().anyMatch(user -> user.getId() == id) &&
- inMemoryFilmStorage.findAllFilms().stream().anyMatch(film -> film.getId() == filmId);
+ public Film getFilm(Integer id) {
+ log.info("Команда: запрос фильма");
+ return filmStorage.getFilm(id);
}
}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java b/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java
index 71373e4..61a5f41 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java
@@ -3,119 +3,58 @@
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
-import ru.yandex.practicum.filmorate.exceptions.UnknownDataException;
import ru.yandex.practicum.filmorate.model.User;
-import ru.yandex.practicum.filmorate.storage.user.InMemoryUserStorage;
+import ru.yandex.practicum.filmorate.storage.user.UserDbStorage;
import java.util.Collection;
-import java.util.Set;
-import java.util.stream.Collectors;
@Service
@Slf4j
public class UserService {
- private final InMemoryUserStorage inMemoryUserStorage;
+ private final UserDbStorage userDbStorage;
@Autowired
- public UserService(InMemoryUserStorage userStorage) {
- this.inMemoryUserStorage = userStorage;
+ public UserService(UserDbStorage userStorage) {
+ this.userDbStorage = userStorage;
}
public Collection findAllUsers() {
- return inMemoryUserStorage.findAllUsers();
+ return userDbStorage.findAllUsers();
}
public User createUser(User user) {
- return inMemoryUserStorage.createUser(user);
+ return userDbStorage.createUser(user);
}
public User updateUser(User user) {
- return inMemoryUserStorage.updateUser(user);
+ return userDbStorage.updateUser(user);
}
//добавление в друзья
public User addFriend(Integer id, Integer friendId) {
- if (!checkFriendsAvalaibility(id, friendId)) {
- throw new UnknownDataException("Запрошенные ресурсы отсутствуют. Невозможно добавить в друзья.");
- }
- User updatedFriend = inMemoryUserStorage.findAllUsers().stream()
- .filter(user -> user.getId() == friendId)
- .map(user -> user.addFriend(id))
- .findFirst()
- .orElseThrow(() -> new UnknownDataException("Пользователь отсутствует"));
- inMemoryUserStorage.updateUser(updatedFriend);
-
- User updatedUser = inMemoryUserStorage.findAllUsers().stream()
- .filter(user -> user.getId() == id)
- .map(user -> user.addFriend(friendId))
- .findFirst()
- .orElseThrow(() -> new UnknownDataException("Пользователь отсутствует"));
- log.info("Запрошено добавление в друзья: " + id + ". От пользователя: " + friendId);
- return inMemoryUserStorage.updateUser(updatedUser);
+ log.info("Команда: добавить друга");
+ return userDbStorage.addFriend(id, friendId);
}
//удаление из друзей
public User removeFriend(Integer id, Integer friendId) {
- if (!checkFriendsAvalaibility(id, friendId)) {
- throw new UnknownDataException("Запрошенные ресурсы отсутствуют. Невозможно удалить из друзей.");
- }
- User updatedFriend = inMemoryUserStorage.findAllUsers().stream()
- .filter(user -> user.getId() == friendId)
- .map(user -> user.deleteFriend(id))
- .findFirst()
- .orElseThrow(() -> new UnknownDataException("Пользователь отсутствует"));
- inMemoryUserStorage.updateUser(updatedFriend);
-
- User updatedUser = inMemoryUserStorage.findAllUsers().stream()
- .filter(user -> user.getId() == id)
- .map(user -> user.deleteFriend(friendId))
- .findFirst()
- .orElseThrow(() -> new UnknownDataException("Пользователь отсутствует"));
- log.info("Запрошено удаление из в друзей: " + id + ". От пользователя: " + friendId);
- return inMemoryUserStorage.updateUser(updatedUser);
+ log.info("Команда: удалить друга");
+ return userDbStorage.removeFriend(id, friendId);
}
//вывод списка общих друзей
public Collection getCommonFriends(Integer id, Integer friendId) {
- if (!checkFriendsAvalaibility(id, friendId))
- throw new UnknownDataException("Запрошенные ресурсы отсутствуют. Невозможно сформировать список общих друзей.");
- User firstUser = inMemoryUserStorage.findAllUsers().stream()
- .filter(user -> user.getId() == id)
- .findFirst()
- .orElse(null);
- User secondUser = inMemoryUserStorage.findAllUsers().stream()
- .filter(user -> user.getId() == friendId)
- .findFirst()
- .orElse(null);
- Set commonId = firstUser.getFriends().stream()
- .filter(secondUser.getFriends()::contains)
- .collect(Collectors.toSet());
- log.info("Запрошены общие друзья: " + id + ". И: " + friendId);
- return inMemoryUserStorage.findAllUsers().stream()
- .filter(user -> commonId.contains(user.getId()))
- .collect(Collectors.toList());
+ log.info("Команда: получить список общих друзей");
+ return userDbStorage.getCommonFriends(id, friendId);
}
+ //получить друзей
public Collection getFriends(Integer id) {
- if (!(inMemoryUserStorage.findAllUsers().stream().anyMatch(user -> user.getId() == id))) {
- throw new UnknownDataException("Запрошенные ресурсы отсутствуют. Невозможно сформировать список друзей");
- }
- Collection friendsIds = inMemoryUserStorage.findAllUsers().stream()
- .filter(user -> user.getId() == id)
- .flatMap(user -> user.getFriends().stream())
- .collect(Collectors.toSet());
- return inMemoryUserStorage.findAllUsers().stream()
- .filter(user -> friendsIds.contains(user.getId()))
- .collect(Collectors.toSet());
- }
-
- //проверка добавления друга
- private boolean checkFriendsAvalaibility(Integer id, Integer friendId) {
- return inMemoryUserStorage.findAllUsers().stream().anyMatch(user -> user.getId() == id) &&
- inMemoryUserStorage.findAllUsers().stream().anyMatch(user -> user.getId() == friendId);
+ log.info("Команда: получить список друзей");
+ return userDbStorage.getFriends(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
new file mode 100644
index 0000000..9fbfe43
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorage.java
@@ -0,0 +1,323 @@
+package ru.yandex.practicum.filmorate.storage.film;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Primary;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.stereotype.Component;
+import org.springframework.stereotype.Repository;
+import ru.yandex.practicum.filmorate.exceptions.BadDataException;
+import ru.yandex.practicum.filmorate.exceptions.UnknownDataException;
+import ru.yandex.practicum.filmorate.model.Film;
+import ru.yandex.practicum.filmorate.model.Genre;
+import ru.yandex.practicum.filmorate.model.Mpa;
+
+import java.time.LocalDate;
+import java.util.*;
+
+@Repository
+@Qualifier("filmDbStorage")
+@Slf4j
+@Component
+@Primary
+
+public class FilmDbStorage implements FilmStorage {
+
+
+ private final JdbcTemplate jdbcTemplate;
+
+ @Autowired
+ public FilmDbStorage(JdbcTemplate jdbcTemplate) {
+ this.jdbcTemplate = jdbcTemplate;
+ }
+
+ @Override
+ public Collection findAllFilms() {
+ String sql = "SELECT movie_id, movie_name, movie_description, movie_release, movie_duration FROM movie";
+ List films = jdbcTemplate.query(sql, (rs, rowNum) -> {
+ Film film = new Film();
+ film.setId(rs.getInt("movie_id"));
+ film.setName(rs.getString("movie_name"));
+ film.setDescription(rs.getString("movie_description"));
+ film.setReleaseDate(rs.getDate("movie_release").toLocalDate());
+ String durationStr = rs.getString("movie_duration");
+ String[] timeParts = durationStr.split(":");
+ long durationInSeconds = Integer.parseInt(timeParts[0]) * 3600
+ + Integer.parseInt(timeParts[1]) * 60
+ + Integer.parseInt(timeParts[2]);
+ film.setDuration(durationInSeconds);
+ return film;
+ });
+ return films;
+ }
+
+ @Override
+ public Film createFilm(Film film) {
+ validateFilm(film);
+
+ long durationInSeconds = film.getDuration();
+ String durationFormatted = String.format("%02d:%02d:%02d",
+ (durationInSeconds / 3600),
+ (durationInSeconds % 3600) / 60,
+ durationInSeconds % 60);
+
+ film.setId(getNextFilmId());
+
+ String sql = "INSERT INTO movie (movie_id, movie_name, movie_description, movie_release, movie_duration, mpa_id) " +
+ "VALUES (?, ?, ?, ?, ?, ?)";
+ jdbcTemplate.update(sql,
+ film.getId(),
+ film.getName(),
+ film.getDescription(),
+ film.getReleaseDate(),
+ durationFormatted,
+ film.getMpa() != null ? film.getMpa().getId() : null);
+ //проверка на существование жанров
+ if (film.getGenres() != null && !film.getGenres().isEmpty()) {
+ String genreSql = "MERGE INTO movie_genre (movie_id, genre_id) KEY (movie_id, genre_id) VALUES (?, ?)";
+ for (Genre genre : film.getGenres()) {
+ jdbcTemplate.update(genreSql, film.getId(), genre.getId());
+ }
+ }
+ return film;
+ }
+
+
+ @Override
+ public Film updateFilm(Film film) {
+ validateFilm(film);
+ String checkSql = "SELECT COUNT(*) FROM movie WHERE movie_id = ?";
+ Integer count = jdbcTemplate.queryForObject(checkSql, Integer.class, film.getId());
+ if (count == null || count == 0) {
+ throw new UnknownDataException("Фильм с таким ID не найден.");
+ }
+ String sql = "UPDATE movie SET movie_name = ?, movie_description = ?, movie_release = ?, movie_duration = ? WHERE movie_id = ?";
+ long durationInSeconds = film.getDuration();
+ String durationFormatted = String.format("%02d:%02d:%02d",
+ (durationInSeconds / 3600),
+ (durationInSeconds % 3600) / 60,
+ durationInSeconds % 60);
+ jdbcTemplate.update(sql,
+ film.getName(),
+ film.getDescription(),
+ film.getReleaseDate(),
+ durationFormatted,
+ film.getId());
+ return film;
+ }
+
+
+ private void validateFilm(Film film) {
+ if (film == null) {
+ throw new BadDataException("Тело запроса не должно быть пустым");
+ }
+ if (film.getDescription() == null || film.getDescription().isBlank()) {
+ throw new BadDataException("Описание не может быть пустым");
+ }
+ if (film.getName() == null || film.getName().isBlank()) {
+ throw new BadDataException("Имя не может быть пустым");
+ }
+ if (film.getDescription().length() > 200) {
+ throw new BadDataException("Число символов в описании не должно превышать 200");
+ }
+ if (film.getReleaseDate().isBefore(LocalDate.of(1895, 12, 28))) {
+ throw new BadDataException("Дата выхода фильма не должна быть раньше дня рождения кинематорграфа");
+ }
+ if (film.getDuration() < 1) {
+ throw new BadDataException("Неверная длительность фильма");
+ }
+ //Блок для проверки жанра и рейтинга
+ if (film.getGenres() != null) {
+ for (Genre genre : film.getGenres()) {
+ if (genre.getId() > 6) {
+ throw new BadDataException("Некорректный id жанра: " + genre.getId());
+ }
+ }
+ }
+ if (film.getMpa() != null && film.getMpa().getId() > 5) {
+ throw new BadDataException("Некорректный id MPA: " + film.getMpa().getId());
+ }
+ }
+
+ //Метод для получения нового уникального ID для пользователя
+ private Integer getNextFilmId() {
+ String sql = "SELECT COALESCE(MAX(movie_id), 0) FROM movie";
+ Long currentMaxId = jdbcTemplate.queryForObject(sql, Long.class);
+ return Math.toIntExact(currentMaxId + 1);
+ }
+
+ // Добавляем лайк
+ public Film addLike(Integer filmId, Integer userId) {
+ if (!((checkMovie(filmId)) && checkUser(userId))) {
+ throw new UnknownDataException("Фильм или пользователь отсутствует");
+ }
+ String checkLikeQuery = "SELECT COUNT(*) FROM movie_rating WHERE movie_id = ? AND user_id = ?";
+ int likeCount = jdbcTemplate.queryForObject(checkLikeQuery, Integer.class, filmId, userId);
+ if (likeCount > 0) {
+ throw new UnknownDataException("Пользователь уже поставил лайк этому фильму.");
+ }
+ String insertLikeQuery = "INSERT INTO movie_rating (movie_id, user_id) VALUES (?, ?)";
+ jdbcTemplate.update(insertLikeQuery, filmId, userId);
+ Film film = getFilmById(filmId);
+ if (film != null) {
+ if (film.getWhoLikes() == null) {
+ film.setWhoLikes(new HashSet<>());
+ }
+ film.getWhoLikes().add(userId);
+ film.setRating(film.getRating() + 1);
+ }
+
+ return film;
+ }
+
+ //Метод гета фильма по id из таблицы
+ private Film getFilmById(Integer filmId) {
+ String sql = "SELECT movie_id, movie_name, movie_description, movie_release, movie_duration " +
+ "FROM movie WHERE movie_id = ?";
+
+ return jdbcTemplate.queryForObject(sql, (rs, rowNum) -> {
+ Film film = new Film();
+ film.setId(rs.getInt("movie_id"));
+ film.setName(rs.getString("movie_name"));
+ film.setDescription(rs.getString("movie_description"));
+ film.setReleaseDate(rs.getDate("movie_release").toLocalDate());
+ java.sql.Time time = rs.getTime("movie_duration");
+ long durationInSeconds = time != null ? time.getTime() / 1000 : 0;
+ film.setDuration(durationInSeconds);
+ film.setWhoLikes(new HashSet<>());
+ film.setRating(0);
+ return film;
+ }, filmId);
+ }
+
+ //Вывод рейтинга фильма
+ public Collection top(Integer count) {
+ log.info("Запрошен топ фильмов в количестве: " + count);
+ String sql = "SELECT m.movie_id, m.movie_name, m.movie_description, m.movie_release, m.movie_duration, " +
+ "COUNT(r.user_id) AS like_count " +
+ "FROM movie m " +
+ "LEFT JOIN movie_rating r ON m.movie_id = r.movie_id " +
+ "GROUP BY m.movie_id " +
+ "ORDER BY like_count DESC " +
+ "LIMIT ?";
+ List films = jdbcTemplate.query(sql, (rs, rowNum) -> {
+ Film film = new Film();
+ film.setId(rs.getInt("movie_id"));
+ film.setName(rs.getString("movie_name"));
+ film.setDescription(rs.getString("movie_description"));
+ film.setReleaseDate(rs.getDate("movie_release").toLocalDate());
+ film.setDuration(rs.getTime("movie_duration").toLocalTime().toSecondOfDay());
+ film.setRating(rs.getInt("like_count"));
+ film.setWhoLikes(new HashSet<>());
+
+ return film;
+ }, count);
+ return films;
+ }
+
+ //Удаление лайка
+ public Film removeLike(Integer filmId, Integer userId) {
+ String checkLikeQuery = "SELECT COUNT(*) FROM movie_rating WHERE movie_id = ? AND user_id = ?";
+ int likeCount = jdbcTemplate.queryForObject(checkLikeQuery, Integer.class, filmId, userId);
+
+ if (likeCount == 0) {
+ throw new UnknownDataException("Пользователь не ставил лайк этому фильму.");
+ }
+ String deleteLikeQuery = "DELETE FROM movie_rating WHERE movie_id = ? AND user_id = ?";
+ jdbcTemplate.update(deleteLikeQuery, filmId, userId);
+ Film film = getFilmById(filmId);
+ if (film != null) {
+ if (film.getWhoLikes() == null) {
+ film.setWhoLikes(new HashSet<>());
+ }
+ film.getWhoLikes().remove(userId);
+ film.setRating(film.getRating() - 1);
+ }
+ return film;
+ }
+
+ public Boolean checkMovie(int filmId) {
+ String sql = "SELECT COUNT(*) FROM movie WHERE movie_id = ?";
+ int count = jdbcTemplate.queryForObject(sql, Integer.class, filmId);
+ return count > 0;
+ }
+
+ public Boolean checkUser(int id) {
+ String sql = "SELECT COUNT(*) FROM users WHERE user_id = ?";
+ int count = jdbcTemplate.queryForObject(sql, Integer.class, id);
+ return count > 0;
+ }
+
+
+ public List getAllGenres() {
+ log.info("Сработал метод запроса жанров");
+ String sql = "SELECT genre_id, genre_name FROM genre";
+ return jdbcTemplate.query(sql, (rs, rowNum) ->
+ new Genre(rs.getInt("genre_id"), rs.getString("genre_name")));
+ }
+
+ public Genre getGenre(Integer id) {
+ if (id > 6) {
+ throw new UnknownDataException("Запрашиваемый жанр отсутствует");
+ }
+ String sql = "SELECT genre_id, genre_name FROM genre WHERE genre_id = ?";
+ return jdbcTemplate.queryForObject(sql, (rs, rowNum) ->
+ new Genre(rs.getInt("genre_id"), rs.getString("genre_name")), id);
+ }
+
+ public Collection getAllMpa() {
+ String sql = "SELECT mpa_id, mpa_name FROM mpa";
+ return jdbcTemplate.query(sql, (rs, rowNum) ->
+ new Mpa(rs.getInt("mpa_id"), rs.getString("mpa_name")));
+ }
+
+ public Mpa getMpa(Integer id) {
+ if (id > 5) {
+ throw new UnknownDataException("Запрашиваемый жанр отсутствует");
+ }
+ String sql = "SELECT mpa_id, mpa_name FROM mpa WHERE mpa_id = ?";
+ return jdbcTemplate.queryForObject(sql, (rs, rowNum) ->
+ new Mpa(rs.getInt("mpa_id"), rs.getString("mpa_name")), id);
+ }
+
+ //Гетаем фильм
+ public Film getFilm(Integer id) {
+ String sql = "SELECT movie_id, movie_name, movie_description, movie_release, movie_duration, mpa_id " +
+ "FROM movie WHERE movie_id = ?";
+
+ Film film = jdbcTemplate.queryForObject(sql, (rs, rowNum) -> {
+ Film result = new Film();
+ result.setId(rs.getInt("movie_id"));
+ result.setName(rs.getString("movie_name"));
+ result.setDescription(rs.getString("movie_description"));
+ result.setReleaseDate(rs.getDate("movie_release").toLocalDate());
+ result.setDuration(rs.getTime("movie_duration").toLocalTime().toSecondOfDay());
+
+ int mpaId = rs.getInt("mpa_id");
+ if (mpaId != 0) {
+ result.setMpa(getMpa(mpaId));
+ }
+
+ return result;
+ }, id);
+
+ if (film == null) {
+ throw new UnknownDataException("Фильм с ID " + id + " не найден.");
+ }
+ film.setGenres(getGenresByFilmId(id));
+ return film;
+ }
+
+ //Вытаскиваем жанры по id фильма
+ private Collection getGenresByFilmId(int filmId) {
+ String sql = "SELECT g.genre_id, g.genre_name " +
+ "FROM movie_genre mg " +
+ "JOIN genre g ON mg.genre_id = g.genre_id " +
+ "WHERE mg.movie_id = ?";
+ Set genres = new HashSet<>(jdbcTemplate.query(sql, (rs, rowNum) ->
+ new Genre(rs.getInt("genre_id"), rs.getString("genre_name")), filmId));
+ return genres.isEmpty() ? Collections.emptyList() : genres;
+ }
+
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/user/InMemoryUserStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/user/InMemoryUserStorage.java
index 9269bf3..41a8471 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/storage/user/InMemoryUserStorage.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/user/InMemoryUserStorage.java
@@ -15,6 +15,7 @@
@Component
@Slf4j
+
public class InMemoryUserStorage implements UserStorage {
private final Map users = new HashMap<>();
diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/user/UserDbStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/user/UserDbStorage.java
new file mode 100644
index 0000000..36ef6fb
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/user/UserDbStorage.java
@@ -0,0 +1,242 @@
+package ru.yandex.practicum.filmorate.storage.user;
+
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Primary;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Component;
+import org.springframework.stereotype.Repository;
+import org.springframework.web.bind.annotation.*;
+import ru.yandex.practicum.filmorate.exceptions.BadDataException;
+import ru.yandex.practicum.filmorate.exceptions.UnknownDataException;
+import ru.yandex.practicum.filmorate.model.User;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.time.LocalDate;
+import java.util.*;
+import java.util.stream.Collectors;
+
+@Repository
+@Qualifier("userDbStorage")
+@Component
+@Slf4j
+@Primary
+public class UserDbStorage implements UserStorage {
+
+ private final JdbcTemplate jdbcTemplate;
+
+ @Autowired
+ public UserDbStorage(JdbcTemplate jdbcTemplate) {
+ this.jdbcTemplate = jdbcTemplate;
+ }
+
+ //Получение всех пользователей
+ @Override
+ public Collection findAllUsers() {
+ log.info("findAllUsers");
+ String sql = "SELECT user_id, user_name, user_email, user_birthdate, user_login FROM users";
+ return jdbcTemplate.query(sql, new RowMapper() {
+ @Override
+ public User mapRow(java.sql.ResultSet rs, int rowNum) throws java.sql.SQLException {
+ User user = new User();
+ user.setId(rs.getInt("user_id"));
+ user.setName(rs.getString("user_name"));
+ user.setEmail(rs.getString("user_email"));
+ user.setBirthday(rs.getDate("user_birthdate").toLocalDate());
+ user.setLogin(rs.getString("user_login"));
+ return user;
+ }
+ });
+ }
+
+ //Создание пользователя
+ @Override
+ public User createUser(@RequestBody User user) {
+ log.info("createUser");
+ validateUser(user);
+ user.setId(getNextUserId());
+ String sql = "INSERT INTO users (user_id, user_name, user_email, user_birthdate, user_login) " +
+ "VALUES (?, ?, ?, ?, ?)";
+ jdbcTemplate.update(sql,
+ user.getId(),
+ user.getName(),
+ user.getEmail(),
+ user.getBirthday(),
+ user.getLogin());
+ log.info("Новый пользователь создан с ID: " + user.getId());
+ return user;
+ }
+
+ //Обновление пользователя
+ @Override
+ public User updateUser(@RequestBody User user) {
+ log.info("updateUser");
+ validateUser(user);
+ if (!userExists(user.getId())) {
+ throw new UnknownDataException("Пользователь отсутствует");
+ }
+ String sql = "UPDATE users SET user_name = ?, user_email = ?, user_birthdate = ?, user_login = ? WHERE user_id = ?";
+
+ jdbcTemplate.update(sql,
+ user.getName(),
+ user.getEmail(),
+ user.getBirthday(),
+ user.getLogin(),
+ user.getId());
+
+ log.info("Пользователь с ID " + user.getId() + " обновлён");
+ return user;
+ }
+
+ //Метод для получения нового уникального ID для пользователя
+ private Integer getNextUserId() {
+ log.info("getNextUserId");
+ String sql = "SELECT COALESCE(MAX(user_id), 0) FROM users";
+ Long currentMaxId = jdbcTemplate.queryForObject(sql, Long.class);
+ return Math.toIntExact(currentMaxId + 1);
+ }
+
+ //Метод проверки создания пользователя
+ public void validateUser(User user) {
+ log.info("validateUser");
+ if (user.getLogin() == null || user.getLogin().isBlank() || user.getLogin().contains(" ")) {
+ throw new BadDataException("Логин не может быть пустым или содержать пробелы");
+ }
+ if (user.getName() == null || user.getName().isBlank()) {
+ user.setName(user.getLogin());
+ }
+ if (user.getEmail() == null || user.getEmail().isBlank()) {
+ throw new BadDataException("Адрес электронной почты не может быть пустым");
+ }
+ if (!user.getEmail().contains("@")) {
+ throw new BadDataException("Неверный адрес электронной почты");
+ }
+ if (user.getBirthday().isAfter(LocalDate.now())) {
+ throw new BadDataException("Дата рождения в будущем");
+ }
+ }
+
+ //Метод проверки существования id пользователя в базе данных. Если есть - вернёт true
+ private boolean userExists(int userId) {
+ log.info("userExist");
+ String sql = "SELECT COUNT(*) FROM users WHERE user_id = ?";
+ int count = jdbcTemplate.queryForObject(sql, Integer.class, userId);
+ return count > 0;
+ }
+
+ public boolean checkFriendsAvalaibility(Integer id, Integer friendId) {
+ log.info("checkFriendAvailability");
+ String sql = "SELECT COUNT(*) FROM users WHERE user_id IN (?, ?)";
+ Integer count = jdbcTemplate.queryForObject(sql, Integer.class, id, friendId);
+ return count != null && count == 2;
+ }
+
+ public User addFriend(Integer id, Integer friendId) {
+ log.info("addFriend");
+ if (!checkFriendsAvalaibility(id, friendId)) {
+ throw new UnknownDataException("Запрошенные ресурсы отсутствуют. Невозможно добавить в друзья.");
+ }
+ String checkExistenceSql = "SELECT COUNT(*) FROM user_friendlist WHERE user_id = ? AND friend_id = ?";
+ Integer count = jdbcTemplate.queryForObject(checkExistenceSql, Integer.class, id, friendId);
+ if (count != null && count > 0) {
+ throw new UnknownDataException("Дружба уже существует.");
+ }
+
+ String sqlInsert1 = "INSERT INTO user_friendlist (user_id, friend_id) VALUES (?, ?)";
+ jdbcTemplate.update(sqlInsert1, id, friendId);
+ return getUserWithFriends(id);
+ }
+
+ private User getUserWithFriends(Integer userId) {
+ log.info("getUserwithFriends");
+ String sqlUser = "SELECT * FROM users WHERE user_id = ?";
+ User user = jdbcTemplate.queryForObject(sqlUser, this::mapRowToUser, userId);
+ String sqlFriends = "SELECT friend_id FROM user_friendlist WHERE user_id = ?";
+ Set friendIds = new HashSet<>(jdbcTemplate.queryForList(sqlFriends, Integer.class, userId));
+ Set friends = new HashSet<>();
+ for (Integer friendId : friendIds) {
+ User friend = getUserById(friendId);
+ friends.add(friend);
+ }
+ user.setFriends(friends);
+ return user;
+ }
+
+ public User getUserById(Integer id) {
+ log.info("getUserById");
+ String sql = "SELECT * FROM users WHERE user_id = ?";
+ return jdbcTemplate.queryForObject(sql, this::mapRowToUser, id);
+ }
+
+ private User mapRowToUser(ResultSet rs, int rowNum) throws SQLException {
+ log.info("mapRowTOUser");
+ User user = new User();
+ user.setId(rs.getInt("user_id"));
+ user.setEmail(rs.getString("user_email"));
+ user.setLogin(rs.getString("user_login"));
+ user.setName(rs.getString("user_name"));
+ user.setBirthday(rs.getDate("user_birthdate").toLocalDate());
+ return user;
+ }
+
+ //Удаление из друзей. Боже, надо было делать сразу с рассчётом на это. Столько лишних переписываний
+ public User removeFriend(Integer id, Integer friendId) {
+ log.info("removeFriend " + friendId + " у пользователя " + id);
+ if (!checkFriendsAvalaibility(id, friendId)) {
+ throw new UnknownDataException("Запрошенные ресурсы отсутствуют. Невозможно удалить из друзей.");
+ }
+ String sqlDelete = "DELETE FROM user_friendlist WHERE user_id = ? AND friend_id = ?";
+ jdbcTemplate.update(sqlDelete, id, friendId);
+ return getUserWithFriends(id);
+ }
+
+ //Общие друзья
+ public Collection getCommonFriends(Integer id, Integer friendId) {
+ log.info("getCommonFriends");
+ if (!checkFriendsAvalaibility(id, friendId)) {
+ throw new UnknownDataException("Запрошенные ресурсы отсутствуют. Невозможно сформировать список общих друзей.");
+ }
+ String sqlFriendsOfFirstUser = "SELECT friend_id FROM user_friendlist WHERE user_id = ?";
+ Set friendsOfFirstUser = new HashSet<>(jdbcTemplate.queryForList(sqlFriendsOfFirstUser, Integer.class, id));
+ String sqlFriendsOfSecondUser = "SELECT friend_id FROM user_friendlist WHERE user_id = ?";
+ Set friendsOfSecondUser = new HashSet<>(jdbcTemplate.queryForList(sqlFriendsOfSecondUser, Integer.class, friendId));
+ friendsOfFirstUser.retainAll(friendsOfSecondUser);
+ if (friendsOfFirstUser.isEmpty()) {
+ return new ArrayList<>();
+ }
+ String sqlCommonFriends = String.format(
+ "SELECT * FROM users WHERE user_id IN (%s)",
+ friendsOfFirstUser.stream().map(String::valueOf).collect(Collectors.joining(", "))
+ );
+ return jdbcTemplate.query(sqlCommonFriends, this::mapRowToUser);
+ }
+
+ public Collection getFriends(Integer id) {
+ log.info("getFriends");
+ String sqlCheckUser = "SELECT COUNT(*) FROM users WHERE user_id = ?";
+ int userCount = jdbcTemplate.queryForObject(sqlCheckUser, Integer.class, id);
+ if (userCount == 0) {
+ throw new UnknownDataException("Запрошенные ресурсы отсутствуют. Невозможно сформировать список друзей");
+ }
+ String sqlFriendsIds = "SELECT friend_id FROM user_friendlist WHERE user_id = ?";
+ List friendsIds = jdbcTemplate.queryForList(sqlFriendsIds, Integer.class, id);
+ if (friendsIds.isEmpty()) {
+ return new ArrayList<>();
+ }
+ String sqlFriendsData = String.format(
+ "SELECT * FROM users WHERE user_id IN (%s)",
+ friendsIds.stream().map(String::valueOf).collect(Collectors.joining(", "))
+ );
+ List friends = jdbcTemplate.query(sqlFriendsData, this::mapRowToUser);
+ for (User friend : friends) {
+ Set friendSet = new HashSet<>(getUserWithFriends(friend.getId()).getFriends());
+ friend.setFriends(friendSet);
+ }
+ return friends;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/resources/application-test.yml b/src/main/resources/application-test.yml
new file mode 100644
index 0000000..691a082
--- /dev/null
+++ b/src/main/resources/application-test.yml
@@ -0,0 +1,9 @@
+spring:
+ datasource:
+ url: jdbc:h2:mem:testdb
+ driver-class-name: org.h2.Driver
+ username: sa
+ password:
+ jpa:
+ hibernate:
+ ddl-auto: none
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
deleted file mode 100644
index 8b13789..0000000
--- a/src/main/resources/application.properties
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 0e3dc5c..23272ef 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -5,6 +5,26 @@ logbook:
type: request
match:
- path: /api/sensitive
+
logging:
level:
- org.zalando.logbook: WARN
\ No newline at end of file
+ org.zalando.logbook: WARN
+
+spring:
+ sql:
+ init:
+ mode: ALWAYS
+
+ datasource:
+ url: jdbc:h2:file:./db/filmorate
+ driver-class-name: org.h2.Driver
+ username: sa
+ password: password
+
+ h2:
+ console:
+ enabled: true
+
+ jpa:
+ hibernate:
+ ddl-auto: update
diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql
new file mode 100644
index 0000000..9130d69
--- /dev/null
+++ b/src/main/resources/schema.sql
@@ -0,0 +1,63 @@
+CREATE TABLE IF NOT EXISTS movie (
+ movie_id INTEGER PRIMARY KEY,
+ movie_name VARCHAR,
+ movie_description VARCHAR,
+ movie_release DATE,
+ movie_duration TIME,
+ genre_id INTEGER,
+ mpa_id INTEGER
+);
+
+CREATE TABLE IF NOT EXISTS genre (
+ genre_id INTEGER PRIMARY KEY,
+ genre_name VARCHAR
+);
+
+CREATE TABLE IF NOT EXISTS movie_genre (
+ movie_id INTEGER,
+ genre_id INTEGER,
+ PRIMARY KEY (movie_id, genre_id),
+ FOREIGN KEY (movie_id) REFERENCES movie(movie_id) ON DELETE CASCADE,
+ FOREIGN KEY (genre_id) REFERENCES genre(genre_id) ON DELETE CASCADE
+);
+
+MERGE INTO genre (genre_id, genre_name) KEY(genre_id) VALUES
+ (1, 'Комедия'),
+ (2, 'Драма'),
+ (3, 'Мультфильм'),
+ (4, 'Триллер'),
+ (5, 'Документальный'),
+ (6, 'Боевик');
+
+CREATE TABLE IF NOT EXISTS mpa (
+ mpa_id INTEGER PRIMARY KEY,
+ mpa_name VARCHAR
+);
+
+MERGE INTO mpa (mpa_id, mpa_name) KEY(mpa_id) VALUES
+ (1, 'G'),
+ (2, 'PG'),
+ (3, 'PG-13'),
+ (4, 'R'),
+ (5, 'NC-17');
+
+
+CREATE TABLE IF NOT EXISTS users (
+ user_id INTEGER PRIMARY KEY,
+ user_name VARCHAR,
+ user_email VARCHAR,
+ user_birthdate DATE,
+ user_login VARCHAR
+);
+
+CREATE TABLE IF NOT EXISTS user_friendlist (
+ user_id INTEGER,
+ friend_id INTEGER,
+ PRIMARY KEY (user_id, friend_id)
+);
+
+CREATE TABLE IF NOT EXISTS movie_rating (
+ movie_id INTEGER,
+ user_id INTEGER,
+ PRIMARY KEY (movie_id, user_id)
+);
diff --git a/src/test/java/ru/yandex/practicum/filmorate/DataBaseStorageTests.java b/src/test/java/ru/yandex/practicum/filmorate/DataBaseStorageTests.java
new file mode 100644
index 0000000..3a3bf9b
--- /dev/null
+++ b/src/test/java/ru/yandex/practicum/filmorate/DataBaseStorageTests.java
@@ -0,0 +1,202 @@
+package ru.yandex.practicum.filmorate;
+
+import lombok.RequiredArgsConstructor;
+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.Film;
+import ru.yandex.practicum.filmorate.model.User;
+import ru.yandex.practicum.filmorate.storage.film.FilmDbStorage;
+import ru.yandex.practicum.filmorate.storage.user.UserDbStorage;
+
+import java.time.LocalDate;
+import java.util.Collection;
+import java.util.Optional;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+@JdbcTest
+@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.ANY)
+@Import({UserDbStorage.class, FilmDbStorage.class})
+@RequiredArgsConstructor(onConstructor_ = @Autowired)
+class DataBaseStorageTests {
+ private final UserDbStorage userStorage;
+ private final FilmDbStorage filmStorage;
+
+
+ @Test
+ void createAndFindUserInDataBase() {
+ User newUser = new User();
+ newUser.setEmail("test@qwerty.com");
+ newUser.setLogin("testuser");
+ newUser.setName("Test User");
+ newUser.setBirthday(LocalDate.of(1990, 1, 1));
+
+ User createdUser = userStorage.createUser(newUser);
+ Optional retrievedUser = Optional.ofNullable(userStorage.getUserById(createdUser.getId()));
+
+ assertThat(retrievedUser).isPresent();
+ assertThat(retrievedUser.get().getEmail()).isEqualTo("test@qwerty.com");
+ }
+
+ @Test
+ void createUserUpdateUserInDatabase() {
+ User newUser = new User();
+ newUser.setEmail("test@drhdhr.com");
+ newUser.setLogin("test");
+ newUser.setName("Test Test");
+ newUser.setBirthday(LocalDate.of(1995, 5, 15));
+
+ User createdUser = userStorage.createUser(newUser);
+ createdUser.setName("New Test");
+ User updatedUser = userStorage.updateUser(createdUser);
+
+ assertThat(updatedUser.getName()).isEqualTo("New Test");
+ }
+
+ @Test
+ void createAndFindAllUsers() {
+ User user1 = new User();
+ user1.setEmail("user1@dhrhd.com");
+ user1.setLogin("user1");
+ user1.setName("User One");
+ user1.setBirthday(LocalDate.of(1990, 1, 1));
+
+ User user2 = new User();
+ user2.setEmail("user2@hhdhdr.com");
+ user2.setLogin("user2");
+ user2.setName("User Two");
+ user2.setBirthday(LocalDate.of(1992, 2, 2));
+
+ userStorage.createUser(user1);
+ userStorage.createUser(user2);
+
+ Collection users = userStorage.findAllUsers();
+ assertEquals(2, users.size());
+ }
+
+ @Test
+ void getCommonFriendsFromDatabase() {
+ User user1 = new User();
+ user1.setEmail("user1@tsw.com");
+ user1.setLogin("user1");
+ user1.setName("User One");
+ user1.setBirthday(LocalDate.of(1990, 1, 1));
+
+ User user2 = new User();
+ user2.setEmail("user2@afawf.com");
+ user2.setLogin("user2");
+ user2.setName("User Two");
+ user2.setBirthday(LocalDate.of(1992, 2, 2));
+
+ User user3 = new User();
+ user3.setEmail("user3@blabla.com");
+ user3.setLogin("user3");
+ user3.setName("User Three");
+ user3.setBirthday(LocalDate.of(1993, 3, 3));
+
+ User createdUser1 = userStorage.createUser(user1);
+ User createdUser2 = userStorage.createUser(user2);
+ User createdUser3 = userStorage.createUser(user3);
+
+ userStorage.addFriend(createdUser1.getId(), createdUser3.getId());
+ userStorage.addFriend(createdUser2.getId(), createdUser3.getId());
+
+ Collection commonFriends = userStorage.getCommonFriends(createdUser1.getId(), createdUser2.getId());
+ assertEquals(1, commonFriends.size());
+ assertTrue(commonFriends.contains(createdUser3));
+ }
+
+ @Test
+ void getFilmFromDatabase() {
+ Film newFilm = new Film();
+ newFilm.setName("H8full 8");
+ newFilm.setDescription("Western Detective");
+ newFilm.setReleaseDate(LocalDate.of(2016, 1, 1));
+ newFilm.setDuration(167);
+
+ Film createdFilm = filmStorage.createFilm(newFilm);
+ Optional retrievedFilm = Optional.ofNullable(filmStorage.getFilm(createdFilm.getId()));
+
+ assertThat(retrievedFilm).isPresent();
+ assertThat(retrievedFilm.get().getName()).isEqualTo("H8full 8");
+ }
+
+ @Test
+ void updateFilmInDataBase() {
+ Film newFilm = new Film();
+ newFilm.setName("Avengers: Infinity War");
+ newFilm.setDescription("Popcorn");
+ newFilm.setReleaseDate(LocalDate.of(2021, 1, 1));
+ newFilm.setDuration(150);
+
+ Film createdFilm = filmStorage.createFilm(newFilm);
+ createdFilm.setName("Avengers: Final");
+ Film updatedFilm = filmStorage.updateFilm(createdFilm);
+
+ assertThat(updatedFilm.getName()).isEqualTo("Avengers: Final");
+ }
+
+ @Test
+ void findAllFilmsInCollection() {
+ Film film1 = new Film();
+ film1.setName("Movie47");
+ film1.setDescription("doubtful");
+ film1.setReleaseDate(LocalDate.of(2020, 1, 1));
+ film1.setDuration(100);
+
+ Film film2 = new Film();
+ film2.setName("Movie48");
+ film2.setDescription("better");
+ film2.setReleaseDate(LocalDate.of(2019, 2, 2));
+ film2.setDuration(110);
+
+ filmStorage.createFilm(film1);
+ filmStorage.createFilm(film2);
+
+ Collection films = filmStorage.findAllFilms();
+ assertEquals(2, films.size());
+ }
+
+ @Test
+ void addLikeAndgetTopFilmsFromDatabase() {
+ Film film1 = new Film();
+ film1.setName("XXX");
+ film1.setDescription("XXX");
+ film1.setReleaseDate(LocalDate.of(2021, 1, 1));
+ film1.setDuration(120);
+
+ Film film2 = new Film();
+ film2.setName("Sinister");
+ film2.setDescription("Description 2");
+ film2.setReleaseDate(LocalDate.of(2021, 2, 2));
+ film2.setDuration(90);
+
+ filmStorage.createFilm(film1);
+ filmStorage.createFilm(film2);
+
+ User newUser = new User();
+ newUser.setEmail("test@qwerty.com");
+ newUser.setLogin("testuser");
+ newUser.setName("Test User1");
+ newUser.setBirthday(LocalDate.of(1990, 1, 1));
+ User createdUser = userStorage.createUser(newUser);
+ User newUser2 = new User();
+ newUser.setEmail("test@fawf.com");
+ newUser.setLogin("testuser");
+ newUser.setName("Test User2");
+ newUser.setBirthday(LocalDate.of(1992, 5, 6));
+ User createdUser2 = userStorage.createUser(newUser);
+
+ filmStorage.addLike(film2.getId(), 1);
+ filmStorage.addLike(film2.getId(), 2);
+
+ Collection topFilms = filmStorage.top(1);
+ assertEquals(1, topFilms.size());
+ assertThat(topFilms.iterator().next().getName()).isEqualTo("Sinister");
+ }
+}
diff --git a/src/test/java/ru/yandex/practicum/filmorate/FilmorateApplicationTests.java b/src/test/java/ru/yandex/practicum/filmorate/FilmorateApplicationTests.java
deleted file mode 100644
index 10075eb..0000000
--- a/src/test/java/ru/yandex/practicum/filmorate/FilmorateApplicationTests.java
+++ /dev/null
@@ -1,132 +0,0 @@
-package ru.yandex.practicum.filmorate;
-
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-import org.springframework.boot.test.context.SpringBootTest;
-import ru.yandex.practicum.filmorate.exceptions.BadDataException;
-import ru.yandex.practicum.filmorate.model.Film;
-import ru.yandex.practicum.filmorate.model.User;
-import ru.yandex.practicum.filmorate.storage.film.InMemoryFilmStorage;
-import ru.yandex.practicum.filmorate.storage.user.InMemoryUserStorage;
-
-import java.time.LocalDate;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-
-import static org.junit.jupiter.api.Assertions.assertThrows;
-
-@SpringBootTest
-class FilmorateApplicationTests {
-
- @Test
- void contextLoads() {
- }
-
- @Test
- void correctFilm() {
- InMemoryFilmStorage master = new InMemoryFilmStorage();
- Film film = new Film(1, "The Hateful eight", "Western", LocalDate.of(2015, 12, 18), 187, 0, new HashSet<>());
- master.createFilm(film);
- Collection filmsCollect = master.findAllFilms();
- ArrayList testFilms = new ArrayList<>(filmsCollect);
- Assertions.assertEquals(film, testFilms.get(0));
- }
-
- @Test
- void incorretDateFilm() {
- InMemoryFilmStorage master = new InMemoryFilmStorage();
- Film film = new Film(0, "The Hateful eight", "Western", LocalDate.of(1890, 12, 18), 187, 0, new HashSet<>());
- BadDataException thrown = assertThrows(
- BadDataException.class,
- () -> master.createFilm(film),
- "Дата выхода фильма не должна быть раньше дня рождения кинематорграфа"
- );
-
- }
-
- @Test
- void incorretFilmNaming() {
- InMemoryFilmStorage master = new InMemoryFilmStorage();
- Film film = new Film(0, " ", "Western", LocalDate.of(2015, 12, 18), 187, 0, new HashSet<>());
- BadDataException thrown = assertThrows(
- BadDataException.class,
- () -> master.createFilm(film),
- "Название не может быть пустым"
- );
- }
-
- @Test
- void incorretFilmDuration() {
- InMemoryFilmStorage master = new InMemoryFilmStorage();
- Film film = new Film(0, "The Hateful Eight", "Western", LocalDate.of(2015, 12, 18), -187, 0, new HashSet<>());
- BadDataException thrown = assertThrows(
- BadDataException.class,
- () -> master.createFilm(film),
- "Неверная длительность фильма"
- );
- }
-
- @Test
- void correctUserTest() {
- InMemoryUserStorage master = new InMemoryUserStorage();
- User user = new User(0, "JD@post.com", "JohnDoe", "John", LocalDate.of(1980, 12, 12), new HashSet<>());
- master.createUser(user);
- Collection usersCollect = master.findAllUsers();
- ArrayList testFilms = new ArrayList<>(usersCollect);
- Assertions.assertEquals(user, testFilms.get(0));
- }
-
- @Test
- void incorrectMailUserTest() {
- InMemoryUserStorage master = new InMemoryUserStorage();
- User user = new User(0, "JDpost.com", "JohnDoe", "John", LocalDate.of(1980, 12, 12), new HashSet<>());
- BadDataException thrown = assertThrows(
- BadDataException.class,
- () -> master.createUser(user),
- "Неверный адрес электронной почты"
- );
- }
-
- @Test
- void incorrectEmptyMailUserTest() {
- InMemoryUserStorage master = new InMemoryUserStorage();
- User user = new User(0, " ", "JohnDoe", "John", LocalDate.of(1980, 12, 12), new HashSet<>());
- BadDataException thrown = assertThrows(
- BadDataException.class,
- () -> master.createUser(user),
- "Адрес электронной почты не может быть пустым"
- );
- }
-
- @Test
- void incorrectLoginUserTest() {
- InMemoryUserStorage master = new InMemoryUserStorage();
- User user = new User(0, "JD@post.com", "John Doe", "John", LocalDate.of(1980, 12, 12), new HashSet<>());
- BadDataException thrown = assertThrows(
- BadDataException.class,
- () -> master.createUser(user),
- "Логин не может быть пустым или содержать пробелы"
- );
- }
-
- @Test
- void incorrectNameUserTest() {
- InMemoryUserStorage master = new InMemoryUserStorage();
- User user = new User(0, "JD@post.com", "JohnDoe", "", LocalDate.of(1980, 12, 12), new HashSet<>());
- master.createUser(user);
- Assertions.assertEquals("JohnDoe", user.getName());
- }
-
- @Test
- void incorrectBirthdateUserTest() {
- InMemoryUserStorage master = new InMemoryUserStorage();
- User user = new User(0, "JD@post.com", "JohnDoe", "John", LocalDate.of(2980, 12, 12), new HashSet<>());
- BadDataException thrown = assertThrows(
- BadDataException.class,
- () -> master.createUser(user),
- "Дата рождения в будущем"
- );
- }
-}
-