diff --git a/FilmorateDatabase.png b/FilmorateDatabase.png
index fb11672..82b8999 100644
Binary files a/FilmorateDatabase.png and b/FilmorateDatabase.png differ
diff --git a/README.md b/README.md
index 922d267..986bb4a 100644
--- a/README.md
+++ b/README.md
@@ -48,16 +48,6 @@ user_id integer (Первичный ключ): Идентификатор пол
friend_id integer (Внешний ключ): Идентификатор другого пользователя(потенциального друга).
-status_id integer (Внешний ключ): Идентификатор статуса дружбы между пользователями.
-
-## Таблица friendship_status.
-
-### Хранит информацию о статусах дружбы.
-
-status_id integer (Первичный ключ): Идентификатор статуса дружбы между пользователями.
-
-status_name varchar: Наименование статуса.
-
## Таблица film_likes.
### Хранит информацию о лайках, которые пользователи поставили фильмам.
@@ -66,21 +56,21 @@ user_id integer (Первичный ключ): Идентификатор пол
film_id integer (Внешний ключ): Идентификатор фильма.
-## Таблица film_ganres.
+## Таблица film_genres.
### Хранит информацию о жанре фильма.
film_id integer (Первичный ключ): Идентификатор фильма.
-ganre_id integer (Внешний ключ): Идентификатор жанра.
+genre_id integer (Внешний ключ): Идентификатор жанра.
-## Таблица ganre.
+## Таблица genre.
### Хранит информацию о жанрах.
-ganre_id integer (Первичный ключ): Идентификатор жанра.
+genre_id integer (Первичный ключ): Идентификатор жанра.
-ganre_name varchar: Наименование жанра.
+genre_name varchar: Наименование жанра.
## Таблица mpa.
diff --git a/pom.xml b/pom.xml
index 9522a8a..937f887 100644
--- a/pom.xml
+++ b/pom.xml
@@ -17,6 +17,7 @@
21
+
org.springframework.boot
spring-boot-starter-web
@@ -26,17 +27,31 @@
org.springframework.boot
spring-boot-starter-validation
-
- org.projectlombok
- lombok
- provided
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ com.h2database
+ h2
+ runtime
+
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
org.springframework.boot
spring-boot-starter-test
test
+
+
+ org.projectlombok
+ lombok
+ provided
+
+
diff --git a/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java b/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java
index 843905e..0166034 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java
@@ -3,10 +3,13 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+
@SpringBootApplication
public class FilmorateApplication {
public static void main(String[] args) {
SpringApplication.run(FilmorateApplication.class, args);
+
}
+
}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/ErrorHandler.java b/src/main/java/ru/yandex/practicum/filmorate/controller/ErrorHandler.java
index 0b04798..9c4f782 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/controller/ErrorHandler.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/controller/ErrorHandler.java
@@ -3,6 +3,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.http.HttpStatus;
import ru.yandex.practicum.filmorate.exception.DataNotFoundException;
import ru.yandex.practicum.filmorate.model.ErrorResponse;
@@ -32,5 +33,12 @@ public ErrorResponse handleNotFoundException(final DataNotFoundException e) {
}
-}
+ @ExceptionHandler
+ @ResponseStatus(HttpStatus.NOT_FOUND)
+ public ErrorResponse emptyData(final EmptyResultDataAccessException e) {
+ log.info("404 {}", e.getMessage());
+ return new ErrorResponse(e.getMessage());
+
+ }
+}
\ No newline at end of file
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 0f35e51..e5ba330 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java
@@ -2,6 +2,7 @@
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;
@@ -12,7 +13,6 @@
import java.util.Collection;
-import java.util.List;
@RestController
@@ -28,9 +28,14 @@ public Collection getFilms() {
return filmService.getFilms();
}
+ @GetMapping(path = "/films/{id}")
+ public Film getFilmById(@PathVariable("id") int id) {
+ return filmService.getFilmById(id);
+ }
+
@PostMapping(path = "/films")
- public Film createFilm(@Valid @RequestBody Film film) throws ValidationException {
+ public Film createFilm(@Valid @RequestBody Film film) throws ValidationException, DataNotFoundException {
return filmService.createFilm(film);
}
@@ -53,9 +58,9 @@ public void deleteLike(@PathVariable("id") int id, @PathVariable("userId") int u
}
@GetMapping(path = "/films/popular")
- public List getPopularFilms(@RequestParam(defaultValue = "10") int count) {
+ public Collection getPopularFilms(@RequestParam(defaultValue = "10") int count) {
return filmService.getPopularFilms(count);
}
-}
+}
\ No newline at end of file
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..ec90f27
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/controller/GenreController.java
@@ -0,0 +1,30 @@
+package ru.yandex.practicum.filmorate.controller;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import ru.yandex.practicum.filmorate.exception.DataNotFoundException;
+import ru.yandex.practicum.filmorate.model.Genre;
+import ru.yandex.practicum.filmorate.service.GenreService;
+
+import java.util.Collection;
+
+@RestController
+@RequestMapping
+@Slf4j
+@RequiredArgsConstructor
+public class GenreController {
+ private final GenreService genreService;
+
+ @GetMapping(path = "/genres/{id}")
+ public Genre getGenreById(@PathVariable("id") int id) throws DataNotFoundException {
+ return genreService.getGenreById(id);
+
+ }
+
+ @GetMapping(path = "/genres")
+ public Collection getGenres() {
+ return genreService.getGenres();
+
+ }
+}
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..a6c7f8b
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/controller/MpaController.java
@@ -0,0 +1,34 @@
+package ru.yandex.practicum.filmorate.controller;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+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.exception.DataNotFoundException;
+import ru.yandex.practicum.filmorate.model.Mpa;
+import ru.yandex.practicum.filmorate.service.MpaService;
+
+import java.util.Collection;
+
+@RestController
+@RequestMapping
+@Slf4j
+@RequiredArgsConstructor
+public class MpaController {
+
+ private final MpaService mpaService;
+
+ @GetMapping(path = "/mpa/{id}")
+ public Mpa getMpaById(@PathVariable("id") int id) throws DataNotFoundException {
+ return mpaService.getById(id);
+
+ }
+
+ @GetMapping(path = "/mpa")
+ public Collection getAllMpa() {
+ return mpaService.getAllMpa();
+
+ }
+}
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 7b3d42d..25db820 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java
@@ -59,4 +59,4 @@ public List getUserFriends(@PathVariable("id") int id) throws DataNotFound
public List getCommonFriends(@PathVariable("id") int id, @PathVariable("otherId") int otherId) throws DataNotFoundException {
return userService.getCommonFriends(id, otherId);
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/ru/yandex/practicum/filmorate/exception/DataNotFoundException.java b/src/main/java/ru/yandex/practicum/filmorate/exception/DataNotFoundException.java
index 589a8d6..6050fca 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/exception/DataNotFoundException.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/exception/DataNotFoundException.java
@@ -4,4 +4,4 @@ public class DataNotFoundException extends Exception {
public DataNotFoundException(String message) {
super(message);
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/ru/yandex/practicum/filmorate/exception/ValidationException.java b/src/main/java/ru/yandex/practicum/filmorate/exception/ValidationException.java
index fd0100a..25158c8 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/exception/ValidationException.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/exception/ValidationException.java
@@ -4,4 +4,4 @@ public class ValidationException extends Exception {
public ValidationException(String message) {
super(message);
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/ErrorResponse.java b/src/main/java/ru/yandex/practicum/filmorate/model/ErrorResponse.java
index aea38a7..88afe64 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/model/ErrorResponse.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/model/ErrorResponse.java
@@ -17,4 +17,4 @@ public String toString() {
return
"error= " + error;
}
-}
+}
\ 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 640bdbf..2b11c3d 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/model/Film.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/model/Film.java
@@ -1,27 +1,29 @@
package ru.yandex.practicum.filmorate.model;
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import lombok.Setter;
+import jakarta.validation.constraints.NotNull;
+import lombok.*;
import java.time.LocalDate;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
+
+import java.util.LinkedHashSet;
@Getter
@Setter
-@RequiredArgsConstructor
+@AllArgsConstructor
+@NoArgsConstructor
+@ToString
+@EqualsAndHashCode(of = "id")
public class Film {
private Integer id;
private String name;
private String description;
+ @NotNull
private LocalDate releaseDate;
private Long duration;
+ @NotNull
private Mpa mpa;
- private Set likes = new HashSet<>();
- private Collection genres;
+ private LinkedHashSet genres;
-}
+}
\ No newline at end of file
diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/FriendshipStatus.java b/src/main/java/ru/yandex/practicum/filmorate/model/FriendshipStatus.java
deleted file mode 100644
index 2afafc6..0000000
--- a/src/main/java/ru/yandex/practicum/filmorate/model/FriendshipStatus.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package ru.yandex.practicum.filmorate.model;
-
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.Setter;
-
-@Getter
-@Setter
-@NoArgsConstructor
-public class FriendshipStatus {
- public Long id;
- public String name;
-}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/Genre.java b/src/main/java/ru/yandex/practicum/filmorate/model/Genre.java
index a298ddf..b900188 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/model/Genre.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/model/Genre.java
@@ -1,14 +1,14 @@
package ru.yandex.practicum.filmorate.model;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.Setter;
+import lombok.*;
@Setter
@Getter
@NoArgsConstructor
+@AllArgsConstructor
+@EqualsAndHashCode
public class Genre {
- private int id;
+ private Integer id;
private String name;
-}
+}
\ No newline at end of file
diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/Mpa.java b/src/main/java/ru/yandex/practicum/filmorate/model/Mpa.java
index 146e154..58150b0 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/model/Mpa.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/model/Mpa.java
@@ -8,7 +8,7 @@
@Setter
@NoArgsConstructor
public class Mpa {
- private int id;
+ private Integer id;
private String name;
-}
+}
\ No newline at end of file
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 75662c3..62e3e76 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/model/User.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/model/User.java
@@ -1,8 +1,6 @@
package ru.yandex.practicum.filmorate.model;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.Setter;
+import lombok.*;
import java.time.LocalDate;
import java.util.HashSet;
@@ -10,7 +8,10 @@
@Getter
@Setter
+@AllArgsConstructor
@NoArgsConstructor
+@ToString
+@EqualsAndHashCode
public class User {
private Set friendsList = new HashSet<>();
private Integer id;
@@ -19,4 +20,4 @@ public class User {
private String name;
private LocalDate birthday;
-}
+}
\ No newline at end of file
diff --git a/src/main/java/ru/yandex/practicum/filmorate/repository/FilmRepository.java b/src/main/java/ru/yandex/practicum/filmorate/repository/FilmRepository.java
new file mode 100644
index 0000000..d4f2678
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/repository/FilmRepository.java
@@ -0,0 +1,26 @@
+package ru.yandex.practicum.filmorate.repository;
+
+import ru.yandex.practicum.filmorate.model.Film;
+
+import java.util.Collection;
+
+public interface FilmRepository {
+ Film save(Film film);
+
+ Film update(Film film);
+
+ Film getById(int id);
+
+ Collection getFilms();
+
+ void setGenre(Film film);
+
+
+ void getGenre(Film film);
+
+ void addLike(int filmId, int userId);
+
+ void deleteLike(int filmId, int userId);
+
+ Collection getPopularFilms(int count);
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/repository/GenreRepository.java b/src/main/java/ru/yandex/practicum/filmorate/repository/GenreRepository.java
new file mode 100644
index 0000000..ea0e9e1
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/repository/GenreRepository.java
@@ -0,0 +1,13 @@
+package ru.yandex.practicum.filmorate.repository;
+
+import ru.yandex.practicum.filmorate.model.Genre;
+
+import java.util.Collection;
+
+public interface GenreRepository {
+ Genre getById(int id);
+
+ Collection getAll();
+
+ boolean genreExist(int id);
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/repository/JdbcFilmRepository.java b/src/main/java/ru/yandex/practicum/filmorate/repository/JdbcFilmRepository.java
new file mode 100644
index 0000000..07a36ae
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/repository/JdbcFilmRepository.java
@@ -0,0 +1,154 @@
+package ru.yandex.practicum.filmorate.repository;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
+import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
+import org.springframework.jdbc.support.GeneratedKeyHolder;
+import org.springframework.stereotype.Repository;
+import ru.yandex.practicum.filmorate.model.Film;
+import ru.yandex.practicum.filmorate.model.Genre;
+import ru.yandex.practicum.filmorate.repository.mappers.FilmRowMapper;
+import ru.yandex.practicum.filmorate.repository.mappers.GenreRowMapper;
+
+import java.util.*;
+
+@Repository
+@RequiredArgsConstructor
+public class JdbcFilmRepository implements FilmRepository {
+ private final NamedParameterJdbcOperations jdbc;
+ private final FilmRowMapper mapper;
+ private final GenreRowMapper genreRowMapper;
+
+
+ @Override
+ public Film save(Film film) {
+
+ GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
+
+ MapSqlParameterSource params = new MapSqlParameterSource();
+ params.addValue("name", film.getName());
+ params.addValue("description", film.getDescription());
+ params.addValue("release_date", film.getReleaseDate());
+ params.addValue("duration", film.getDuration());
+ params.addValue("mpa_id", film.getMpa().getId());
+
+ jdbc.update("INSERT INTO FILMS (NAME, DESCRIPTION, RELEASE_DATE, DURATION, MPA_ID) VALUES(:name,:description ,:release_date ,:duration ,:mpa_id)", params, keyHolder);
+
+ film.setId(keyHolder.getKeyAs(Integer.class));
+ if (film.getGenres() != null) {
+ setGenre(film);
+ }
+ return film;
+ }
+
+ @Override
+ public Film update(Film film) {
+
+ GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
+ MapSqlParameterSource params = new MapSqlParameterSource();
+ params.addValue("name", film.getName());
+ params.addValue("description", film.getDescription());
+ params.addValue("release_date", film.getReleaseDate());
+ params.addValue("duration", film.getDuration());
+ params.addValue("mpa_id", film.getMpa().getId());
+ params.addValue("film_id", film.getId());
+ jdbc.update("DELETE FROM FILM_GENRES WHERE FILM_ID=:film_id", params, keyHolder);
+
+ if (film.getGenres() != null) {
+ setGenre(film);
+ }
+ film.setGenres(new LinkedHashSet<>());
+ jdbc.update("UPDATE FILMS SET NAME=:name, DESCRIPTION=:description, RELEASE_DATE=:release_date, DURATION=:duration, MPA_ID=:mpa_id WHERE FILM_ID=:film_id", params, keyHolder);
+
+
+ return film;
+ }
+
+
+ @Override
+ public Film getById(int id) {
+ GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
+ MapSqlParameterSource params = new MapSqlParameterSource();
+ params.addValue("film_id", id);
+ Film film = jdbc.queryForObject("SELECT f.*, m.MPA_NAME FROM FILMS f JOIN MPA m ON f.MPA_ID = m.MPA_ID WHERE f.FILM_ID = :film_id", params, mapper);
+ if (film.getGenres() != null) {
+ getGenre(film);
+ }
+ return film;
+
+
+ }
+
+ @Override
+ public Collection getFilms() {
+ final String query = "SELECT f.*, m.MPA_NAME FROM FILMS f JOIN MPA m ON f.MPA_ID = m.MPA_ID";
+ Collection films = jdbc.query(query, mapper);
+
+
+ for (Film film : films) {
+
+ getGenre(film);
+
+ }
+ return films;
+
+ }
+
+ @Override
+ public void setGenre(Film film) {
+ GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
+ MapSqlParameterSource params = new MapSqlParameterSource();
+ params.addValue("film_id", film.getId());
+ Set duplicateGenres = new HashSet<>();
+
+ if (film.getGenres() != null) {
+ Collection genres = film.getGenres();
+ for (Genre genre : genres) {
+ if (!duplicateGenres.contains(genre)) {
+ params.addValue("genre_id", genre.getId());
+ jdbc.update("INSERT INTO FILM_GENRES (FILM_ID, GENRE_ID) VALUES(:film_id, :genre_id);", params, keyHolder);
+ duplicateGenres.add(genre);
+ }
+ }
+ }
+
+ }
+
+ @Override
+ public void getGenre(Film film) {
+ MapSqlParameterSource params = new MapSqlParameterSource();
+
+ params.addValue("film_id", film.getId());
+
+ Collection genres = jdbc.query("SELECT g.GENRE_ID AS id ,g.GENRE_NAME AS name FROM FILM_GENRES fg JOIN GENRES g ON fg.GENRE_ID = g.GENRE_ID WHERE fg.FILM_ID = :film_id", params, genreRowMapper);
+ LinkedHashSet filmGenres = new LinkedHashSet<>(genres);
+ film.setGenres(filmGenres);
+ }
+
+ @Override
+ public void addLike(int filmId, int userId) {
+ GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
+ MapSqlParameterSource params = new MapSqlParameterSource();
+ params.addValue("user_id", userId);
+ params.addValue("film_id", filmId);
+ jdbc.update("INSERT INTO FILM_LIKES (USER_ID, FILM_ID) VALUES(:user_id, :film_id)", params, keyHolder);
+ }
+
+ @Override
+ public void deleteLike(int filmId, int userId) {
+ GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
+ MapSqlParameterSource params = new MapSqlParameterSource();
+ params.addValue("user_id", userId);
+ params.addValue("film_id", filmId);
+ jdbc.update("DELETE FROM FILM_LIKES WHERE FILM_ID=:film_id AND USER_ID=:user_id;", params, keyHolder);
+ }
+
+ @Override
+ public Collection getPopularFilms(int count) {
+ MapSqlParameterSource params = new MapSqlParameterSource();
+ params.addValue("limit", count);
+ Collection popularFilms = jdbc.query("SELECT f.*, m.MPA_NAME , COUNT(fl.USER_ID) AS likes FROM FILMS f JOIN FILM_LIKES fl ON fl.FILM_ID = f.FILM_ID JOIN MPA m ON m.MPA_ID = f.MPA_ID GROUP BY f.FILM_ID ORDER BY COUNT(fl.USER_ID) DESC LIMIT :limit", params, mapper);
+ return popularFilms;
+
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/repository/JdbcGenreRepository.java b/src/main/java/ru/yandex/practicum/filmorate/repository/JdbcGenreRepository.java
new file mode 100644
index 0000000..42215f9
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/repository/JdbcGenreRepository.java
@@ -0,0 +1,40 @@
+package ru.yandex.practicum.filmorate.repository;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
+import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
+import org.springframework.jdbc.support.GeneratedKeyHolder;
+import org.springframework.stereotype.Repository;
+import ru.yandex.practicum.filmorate.model.Genre;
+import ru.yandex.practicum.filmorate.repository.mappers.GenreRowMapper;
+
+import java.util.Collection;
+
+@Repository
+@RequiredArgsConstructor
+public class JdbcGenreRepository implements GenreRepository {
+
+ private final NamedParameterJdbcOperations jdbc;
+ private final GenreRowMapper mapper;
+
+ @Override
+ public Genre getById(int id) {
+ GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
+ MapSqlParameterSource params = new MapSqlParameterSource();
+ params.addValue("genre_id", id);
+ return jdbc.queryForObject("SELECT * FROM GENRES WHERE GENRE_ID = :genre_id", params, mapper);
+ }
+
+ @Override
+ public Collection getAll() {
+ final String query = "select * FROM GENRES ORDER BY GENRE_ID ";
+ return jdbc.query(query, mapper);
+ }
+
+ @Override
+ public boolean genreExist(int id) {
+ MapSqlParameterSource params = new MapSqlParameterSource();
+ params.addValue("genre_id", id);
+ return Boolean.TRUE.equals(jdbc.queryForObject("SELECT EXISTS(SELECT 1 FROM GENRES WHERE genre_id =:genre_id)", params, boolean.class));
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/repository/JdbcMpaRepository.java b/src/main/java/ru/yandex/practicum/filmorate/repository/JdbcMpaRepository.java
new file mode 100644
index 0000000..6bcd0b2
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/repository/JdbcMpaRepository.java
@@ -0,0 +1,37 @@
+package ru.yandex.practicum.filmorate.repository;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
+import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
+import org.springframework.stereotype.Repository;
+import ru.yandex.practicum.filmorate.model.Mpa;
+import ru.yandex.practicum.filmorate.repository.mappers.MpaRowMapper;
+
+import java.util.Collection;
+
+@Repository
+@RequiredArgsConstructor
+public class JdbcMpaRepository implements MpaRepository {
+ private final NamedParameterJdbcOperations jdbc;
+ private final MpaRowMapper mapper;
+
+ @Override
+ public Mpa getById(int id) {
+ MapSqlParameterSource params = new MapSqlParameterSource();
+ params.addValue("mpa_id", id);
+ return jdbc.queryForObject("SELECT * FROM MPA WHERE MPA_ID = :mpa_id", params, mapper);
+ }
+
+ @Override
+ public Collection getAll() {
+ final String query = "select * FROM MPA ORDER BY MPA_ID";
+ return jdbc.query(query, mapper);
+ }
+
+ @Override
+ public boolean mpaExist(int id) {
+ MapSqlParameterSource params = new MapSqlParameterSource();
+ params.addValue("mpa_id", id);
+ return Boolean.TRUE.equals(jdbc.queryForObject("SELECT EXISTS(SELECT 1 FROM MPA WHERE mpa_id =:mpa_id)", params, boolean.class));
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/repository/JdbcUserRepository.java b/src/main/java/ru/yandex/practicum/filmorate/repository/JdbcUserRepository.java
new file mode 100644
index 0000000..b3ea4b7
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/repository/JdbcUserRepository.java
@@ -0,0 +1,93 @@
+package ru.yandex.practicum.filmorate.repository;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
+import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
+import org.springframework.jdbc.support.GeneratedKeyHolder;
+import org.springframework.stereotype.Repository;
+import ru.yandex.practicum.filmorate.model.User;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+
+@Repository
+@RequiredArgsConstructor
+public class JdbcUserRepository implements UserRepository {
+
+ private final NamedParameterJdbcOperations jdbc;
+ private final RowMapper mapper;
+
+ @Override
+ public User save(User user) {
+ GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
+ MapSqlParameterSource params = new MapSqlParameterSource();
+ params.addValue("login", user.getLogin());
+ params.addValue("email", user.getEmail());
+ params.addValue("user_name", user.getName());
+ params.addValue("birthday", user.getBirthday());
+ jdbc.update("INSERT INTO USERS (LOGIN, EMAIL, USER_NAME, BIRTHDAY) VALUES(:login,:email ,:user_name ,:birthday )", params, keyHolder);
+ user.setId(keyHolder.getKeyAs(Integer.class));
+ return user;
+ }
+
+ @Override
+ public User update(User user) {
+ GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
+ MapSqlParameterSource params = new MapSqlParameterSource();
+ params.addValue("login", user.getLogin());
+ params.addValue("email", user.getEmail());
+ params.addValue("user_name", user.getName());
+ params.addValue("birthday", user.getBirthday());
+ params.addValue("user_id", user.getId());
+ jdbc.update("UPDATE PUBLIC.USERS SET LOGIN=:login, EMAIL=:email, USER_NAME=:user_name, BIRTHDAY=:birthday WHERE USER_ID=:user_id", params, keyHolder);
+ return user;
+
+
+ }
+
+ @Override
+ public Optional getById(int userId) {
+ GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
+ MapSqlParameterSource params = new MapSqlParameterSource();
+ params.addValue("user_id", userId);
+ return Optional.ofNullable(jdbc.queryForObject("SELECT * FROM USERS WHERE USER_ID = :user_id", params, mapper));
+ }
+
+ @Override
+ public Collection getUsers() {
+ final String query = "select * FROM USERS";
+ return jdbc.query(query, mapper);
+
+ }
+
+ @Override
+ public void addFriend(int userId, int friendId) {
+ GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
+ MapSqlParameterSource params = new MapSqlParameterSource();
+ params.addValue("user_id", userId);
+ params.addValue("friend_id", friendId);
+ jdbc.update("INSERT INTO FRIENDS(USER_ID, FRIEND_ID) VALUES(:user_id,:friend_id)", params, keyHolder);
+ }
+
+ @Override
+ public void deleteFriend(int userId, int friendId) {
+ GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
+ MapSqlParameterSource params = new MapSqlParameterSource();
+ params.addValue("user_id", userId);
+ params.addValue("friend_id", friendId);
+ jdbc.update("DELETE FROM FRIENDS WHERE USER_ID=:user_id AND FRIEND_ID=:friend_id", params, keyHolder);
+ }
+
+ @Override
+ public List getUserFriends(int userId) {
+ MapSqlParameterSource params = new MapSqlParameterSource();
+ params.addValue("user_id", userId);
+ return jdbc.query("SELECT * FROM USERS where USER_ID IN (SELECT FRIEND_ID FROM FRIENDS WHERE USER_ID = :user_id) ", params, mapper);
+
+ }
+
+}
+
+
diff --git a/src/main/java/ru/yandex/practicum/filmorate/repository/MpaRepository.java b/src/main/java/ru/yandex/practicum/filmorate/repository/MpaRepository.java
new file mode 100644
index 0000000..1010256
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/repository/MpaRepository.java
@@ -0,0 +1,13 @@
+package ru.yandex.practicum.filmorate.repository;
+
+import ru.yandex.practicum.filmorate.model.Mpa;
+
+import java.util.Collection;
+
+public interface MpaRepository {
+ Mpa getById(int id);
+
+ Collection getAll();
+
+ boolean mpaExist(int id);
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/repository/UserRepository.java b/src/main/java/ru/yandex/practicum/filmorate/repository/UserRepository.java
new file mode 100644
index 0000000..9432e17
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/repository/UserRepository.java
@@ -0,0 +1,23 @@
+package ru.yandex.practicum.filmorate.repository;
+
+import ru.yandex.practicum.filmorate.model.User;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+
+public interface UserRepository {
+ User save(User user);
+
+ User update(User user);
+
+ Optional getById(int userId);
+
+ Collection getUsers();
+
+ void addFriend(int userId, int friendId);
+
+ void deleteFriend(int userId, int friendId);
+
+ List getUserFriends(int userId);
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/repository/mappers/FilmRowMapper.java b/src/main/java/ru/yandex/practicum/filmorate/repository/mappers/FilmRowMapper.java
new file mode 100644
index 0000000..b1fbe8f
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/repository/mappers/FilmRowMapper.java
@@ -0,0 +1,30 @@
+package ru.yandex.practicum.filmorate.repository.mappers;
+
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Component;
+import ru.yandex.practicum.filmorate.model.Film;
+import ru.yandex.practicum.filmorate.model.Mpa;
+
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.LinkedHashSet;
+
+@Component
+public class FilmRowMapper implements RowMapper {
+ @Override
+ public Film mapRow(ResultSet rs, int rowNum) throws SQLException {
+ Mpa mpa = new Mpa();
+ mpa.setId(rs.getInt("mpa_id"));
+ mpa.setName(rs.getString("mpa_name"));
+ Film film = new Film();
+ film.setId(rs.getInt("film_id"));
+ film.setName(rs.getString("name"));
+ film.setDescription(rs.getString("description"));
+ film.setReleaseDate(rs.getDate("release_date").toLocalDate());
+ film.setDuration(rs.getLong("duration"));
+ film.setMpa(mpa);
+ film.setGenres(new LinkedHashSet<>());
+ return film;
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/repository/mappers/GenreRowMapper.java b/src/main/java/ru/yandex/practicum/filmorate/repository/mappers/GenreRowMapper.java
new file mode 100644
index 0000000..7fb7dbb
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/repository/mappers/GenreRowMapper.java
@@ -0,0 +1,19 @@
+package ru.yandex.practicum.filmorate.repository.mappers;
+
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Component;
+import ru.yandex.practicum.filmorate.model.Genre;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+@Component
+public class GenreRowMapper implements RowMapper {
+
+ public Genre mapRow(ResultSet resultSet, int rowNum) throws SQLException {
+ Genre genre = new Genre();
+ genre.setId(resultSet.getInt("genre_id"));
+ genre.setName(resultSet.getString("genre_name"));
+ return genre;
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/repository/mappers/MpaRowMapper.java b/src/main/java/ru/yandex/practicum/filmorate/repository/mappers/MpaRowMapper.java
new file mode 100644
index 0000000..b31ed13
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/repository/mappers/MpaRowMapper.java
@@ -0,0 +1,19 @@
+package ru.yandex.practicum.filmorate.repository.mappers;
+
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Component;
+import ru.yandex.practicum.filmorate.model.Mpa;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+@Component
+public class MpaRowMapper implements RowMapper {
+ @Override
+ public Mpa mapRow(ResultSet rs, int rowNum) throws SQLException {
+ Mpa mpa = new Mpa();
+ mpa.setId(rs.getInt("mpa_id"));
+ mpa.setName(rs.getString("mpa_name"));
+ return mpa;
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/repository/mappers/UserRowMapper.java b/src/main/java/ru/yandex/practicum/filmorate/repository/mappers/UserRowMapper.java
new file mode 100644
index 0000000..f6e2182
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/repository/mappers/UserRowMapper.java
@@ -0,0 +1,22 @@
+package ru.yandex.practicum.filmorate.repository.mappers;
+
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Component;
+import ru.yandex.practicum.filmorate.model.User;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+@Component
+public class UserRowMapper implements RowMapper {
+ @Override
+ public User mapRow(ResultSet rs, int rowNum) throws SQLException {
+ User user = new User();
+ user.setId(rs.getInt("user_id"));
+ user.setLogin(rs.getString("login"));
+ user.setEmail(rs.getString("email"));
+ user.setName(rs.getString("user_name"));
+ user.setBirthday(rs.getDate("birthday").toLocalDate());
+ return user;
+ }
+}
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 0731a2a..2d78e87 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java
@@ -1,73 +1,25 @@
package ru.yandex.practicum.filmorate.service;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
import ru.yandex.practicum.filmorate.exception.DataNotFoundException;
import ru.yandex.practicum.filmorate.exception.ValidationException;
import ru.yandex.practicum.filmorate.model.Film;
-import ru.yandex.practicum.filmorate.model.User;
-import ru.yandex.practicum.filmorate.storage.FilmStorage;
-import java.util.ArrayList;
import java.util.Collection;
-import java.util.Comparator;
-import java.util.List;
-import java.util.stream.Collectors;
-@Service
-@RequiredArgsConstructor
-@Slf4j
-public class FilmService {
+public interface FilmService {
+ Collection getFilms();
- private final FilmStorage filmStorage;
- private final UserService userService;
+ Film getFilmById(int id);
- public Collection getFilms() {
- return filmStorage.getFilms();
- }
+ Film createFilm(Film film) throws ValidationException, DataNotFoundException;
- public Film createFilm(Film film) throws ValidationException {
- return filmStorage.createFilm(film);
- }
+ Film updateFilm(Film newFilm) throws DataNotFoundException;
- public Film updateFilm(Film newFilm) throws DataNotFoundException {
+ void addLike(int filmId, int userId) throws DataNotFoundException;
- return filmStorage.updateFilm(newFilm);
- }
+ void deleteLike(int filmId, int userId) throws DataNotFoundException;
- public void addLike(int id, int userId) throws DataNotFoundException {
- Film film = get(id);
- User user = userService.get(userId);
+ Collection getPopularFilms(int count);
- film.getLikes().add(userId);
- log.info("add like: {}", film);
- }
-
- public void deleteLike(int id, int userId) throws DataNotFoundException {
- Film film = get(id);
- User user = userService.get(userId);
-
- film.getLikes().remove(userId);
- log.info("delete like: {}", film);
- }
-
- public List getPopularFilms(int count) {
- List popularFilms = new ArrayList<>(filmStorage.getFilms());
- popularFilms.sort(FILM_COMPARATOR.reversed());
- return popularFilms.stream().limit(count).collect(Collectors.toList());
-
- }
-
- public static final Comparator FILM_COMPARATOR = new Comparator() {
- @Override
- public int compare(Film o1, Film o2) {
- return o1.getLikes().size() - o2.getLikes().size();
- }
-
- };
-
- public Film get(int filmId) throws DataNotFoundException {
- return filmStorage.get(filmId);
- }
+ Film get(int filmId) throws DataNotFoundException;
}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/FilmServiceImpl.java b/src/main/java/ru/yandex/practicum/filmorate/service/FilmServiceImpl.java
new file mode 100644
index 0000000..3aaa927
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/service/FilmServiceImpl.java
@@ -0,0 +1,95 @@
+package ru.yandex.practicum.filmorate.service;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import ru.yandex.practicum.filmorate.exception.DataNotFoundException;
+import ru.yandex.practicum.filmorate.exception.ValidationException;
+import ru.yandex.practicum.filmorate.model.Film;
+import ru.yandex.practicum.filmorate.model.Genre;
+import ru.yandex.practicum.filmorate.repository.JdbcFilmRepository;
+import ru.yandex.practicum.filmorate.repository.JdbcGenreRepository;
+import ru.yandex.practicum.filmorate.repository.JdbcMpaRepository;
+import ru.yandex.practicum.filmorate.storage.FilmStorage;
+
+import java.time.LocalDate;
+import java.time.Month;
+import java.util.Collection;
+
+@Service
+@RequiredArgsConstructor
+@Slf4j
+public class FilmServiceImpl implements FilmService {
+
+ private final FilmStorage filmStorage;
+ private final JdbcFilmRepository jdbcFilmRepository;
+ private final JdbcGenreRepository jdbcGenreRepository;
+ private final JdbcMpaRepository jdbcMpaRepository;
+ private final UserServiceImpl userService;
+
+ @Override
+ public Collection getFilms() {
+ return jdbcFilmRepository.getFilms();
+ }
+
+ @Override
+ public Film getFilmById(int id) {
+ return jdbcFilmRepository.getById(id);
+ }
+
+ @Override
+ public Film createFilm(Film film) throws ValidationException, DataNotFoundException {
+ LocalDate date = LocalDate.of(1895, Month.DECEMBER, 28);
+ if (film.getName() == null || film.getName().isEmpty()) {
+ throw new ValidationException("Название фильма не может быть пустым");
+ }
+ if (film.getDescription() == null || film.getDescription().length() > 200) {
+ throw new ValidationException("Максимальная длина описания — 200 символов");
+ }
+ if (film.getReleaseDate().isBefore(date)) {
+ throw new ValidationException("Дата релиза — не раньше 28 декабря 1895 года");
+ }
+ if (film.getDuration() <= 0) {
+ throw new ValidationException("Продолжительность фильма должна быть положительным числом");
+ }
+ if (!jdbcMpaRepository.mpaExist(film.getMpa().getId())) {
+ throw new DataNotFoundException("Некорректный возрастной рейтинг");
+ }
+ if (film.getGenres() != null) {
+ for (Genre genre : film.getGenres()) {
+ if (!jdbcGenreRepository.genreExist(genre.getId())) {
+ throw new DataNotFoundException("Некорректный жанр");
+ }
+ }
+ }
+ return jdbcFilmRepository.save(film);
+ }
+
+ @Override
+ public Film updateFilm(Film newFilm) throws DataNotFoundException {
+ jdbcFilmRepository.getById(newFilm.getId());
+ return jdbcFilmRepository.update(newFilm);
+ }
+
+ @Override
+ public void addLike(int filmId, int userId) throws DataNotFoundException {
+ jdbcFilmRepository.addLike(filmId, userId);
+ }
+
+ @Override
+ public void deleteLike(int filmId, int userId) throws DataNotFoundException {
+ jdbcFilmRepository.deleteLike(filmId, userId);
+ }
+
+ @Override
+ public Collection getPopularFilms(int count) {
+ return jdbcFilmRepository.getPopularFilms(count);
+
+ }
+
+
+ @Override
+ public Film get(int filmId) throws DataNotFoundException {
+ return filmStorage.get(filmId);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/GenreService.java b/src/main/java/ru/yandex/practicum/filmorate/service/GenreService.java
new file mode 100644
index 0000000..91ce376
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/service/GenreService.java
@@ -0,0 +1,12 @@
+package ru.yandex.practicum.filmorate.service;
+
+import ru.yandex.practicum.filmorate.exception.DataNotFoundException;
+import ru.yandex.practicum.filmorate.model.Genre;
+
+import java.util.Collection;
+
+public interface GenreService {
+ Genre getGenreById(int id) throws DataNotFoundException;
+
+ Collection getGenres();
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/GenreServiceImpl.java b/src/main/java/ru/yandex/practicum/filmorate/service/GenreServiceImpl.java
new file mode 100644
index 0000000..70146f0
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/service/GenreServiceImpl.java
@@ -0,0 +1,28 @@
+package ru.yandex.practicum.filmorate.service;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import ru.yandex.practicum.filmorate.exception.DataNotFoundException;
+import ru.yandex.practicum.filmorate.model.Genre;
+import ru.yandex.practicum.filmorate.repository.JdbcGenreRepository;
+
+import java.util.Collection;
+
+@Service
+@RequiredArgsConstructor
+public class GenreServiceImpl implements GenreService {
+ private final JdbcGenreRepository jdbcGenreRepository;
+
+ @Override
+ public Genre getGenreById(int id) throws DataNotFoundException {
+ if (!jdbcGenreRepository.genreExist(id)) {
+ throw new DataNotFoundException("Некорректный id");
+ }
+ return jdbcGenreRepository.getById(id);
+ }
+
+ @Override
+ public Collection getGenres() {
+ return jdbcGenreRepository.getAll();
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/MpaService.java b/src/main/java/ru/yandex/practicum/filmorate/service/MpaService.java
new file mode 100644
index 0000000..f701fba
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/service/MpaService.java
@@ -0,0 +1,12 @@
+package ru.yandex.practicum.filmorate.service;
+
+import ru.yandex.practicum.filmorate.exception.DataNotFoundException;
+import ru.yandex.practicum.filmorate.model.Mpa;
+
+import java.util.Collection;
+
+public interface MpaService {
+ Mpa getById(int id) throws DataNotFoundException;
+
+ Collection getAllMpa();
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/MpaServiceImpl.java b/src/main/java/ru/yandex/practicum/filmorate/service/MpaServiceImpl.java
new file mode 100644
index 0000000..1678b31
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/service/MpaServiceImpl.java
@@ -0,0 +1,28 @@
+package ru.yandex.practicum.filmorate.service;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import ru.yandex.practicum.filmorate.exception.DataNotFoundException;
+import ru.yandex.practicum.filmorate.model.Mpa;
+import ru.yandex.practicum.filmorate.repository.JdbcMpaRepository;
+
+import java.util.Collection;
+
+@Service
+@RequiredArgsConstructor
+public class MpaServiceImpl implements MpaService {
+ private final JdbcMpaRepository jdbcMpaRepository;
+
+ @Override
+ public Mpa getById(int id) throws DataNotFoundException {
+ if (!jdbcMpaRepository.mpaExist(id)) {
+ throw new DataNotFoundException("Некорректный id");
+ }
+ return jdbcMpaRepository.getById(id);
+ }
+
+ @Override
+ public Collection getAllMpa() {
+ return jdbcMpaRepository.getAll();
+ }
+}
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 de588ba..4e92a80 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java
@@ -1,83 +1,24 @@
package ru.yandex.practicum.filmorate.service;
-import lombok.RequiredArgsConstructor;
-import org.springframework.stereotype.Service;
import ru.yandex.practicum.filmorate.exception.DataNotFoundException;
import ru.yandex.practicum.filmorate.exception.ValidationException;
import ru.yandex.practicum.filmorate.model.User;
-import ru.yandex.practicum.filmorate.storage.UserStorage;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+public interface UserService {
+ Collection getUsers();
-@Service
-@RequiredArgsConstructor
-public class UserService {
- private final UserStorage userStorage;
+ User createUser(User user) throws ValidationException;
- public Collection getUsers() {
- return userStorage.getUsers();
- }
+ User updateUser(User newUser) throws DataNotFoundException;
- public User createUser(User user) throws ValidationException {
+ void addFriend(int id, int friendId) throws DataNotFoundException;
- return userStorage.createUser(user);
- }
+ void deleteFriend(int id, int friendId) throws DataNotFoundException;
- public User updateUser(User newUser) throws DataNotFoundException {
- return userStorage.updateUser(newUser);
+ List getUserFriends(int id) throws DataNotFoundException;
- }
-
- public void addFriend(int id, int friendId) throws DataNotFoundException {
-
- User user = get(id);
- User friend = get(friendId);
-
- user.getFriendsList().add(friendId);
- friend.getFriendsList().add(id);
- }
-
- public void deleteFriend(Integer id, int friendId) throws DataNotFoundException {
-
- User user = get(id);
- User friend = get(friendId);
-
- user.getFriendsList().remove(friendId);
- friend.getFriendsList().remove(id);
- }
-
- public List getUserFriends(int id) throws DataNotFoundException {
-
- User user = get(id);
-
- List friends = new ArrayList<>();
- for (Integer friend : user.getFriendsList()) {
- friends.add(get(friend));
- }
- return friends;
-
- }
-
- public List getCommonFriends(int id, int otherId) throws DataNotFoundException {
- List userList = getUserFriends(id);
- List otherUserList = getUserFriends(otherId);
- List commonFriends = new ArrayList<>();
- for (User user : userList) {
- if (otherUserList.contains(user)) {
- commonFriends.add(user);
- }
- }
- return commonFriends;
-
-
- }
-
-
- public User get(int userId) throws DataNotFoundException {
-
- return userStorage.get(userId);
- }
+ List getCommonFriends(int id, int otherId) throws DataNotFoundException;
}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/UserServiceImpl.java b/src/main/java/ru/yandex/practicum/filmorate/service/UserServiceImpl.java
new file mode 100644
index 0000000..a90f567
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/service/UserServiceImpl.java
@@ -0,0 +1,87 @@
+package ru.yandex.practicum.filmorate.service;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import ru.yandex.practicum.filmorate.exception.DataNotFoundException;
+import ru.yandex.practicum.filmorate.exception.ValidationException;
+import ru.yandex.practicum.filmorate.model.User;
+import ru.yandex.practicum.filmorate.repository.JdbcUserRepository;
+
+import java.time.LocalDate;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
+
+@Service
+@RequiredArgsConstructor
+public class UserServiceImpl implements UserService {
+
+ private final JdbcUserRepository jdbcUserRepository;
+
+ @Override
+ public Collection getUsers() {
+ return jdbcUserRepository.getUsers();
+ }
+
+ @Override
+ public User createUser(User user) throws ValidationException {
+ if (user.getEmail().isEmpty() || !user.getEmail().contains("@")) {
+ throw new ValidationException("Электронная почта не может быть пустой и должна содержать символ @");
+ }
+ if (user.getLogin() == null || user.getLogin().isEmpty() || user.getLogin().contains(" ")) {
+ throw new ValidationException("Логин не может быть пустым и содержать пробелы");
+ }
+ if (user.getName() == null) {
+ user.setName(user.getLogin());
+ }
+ if (user.getBirthday().isAfter(LocalDate.now())) {
+ throw new ValidationException("Дата рождения не может быть в будущем");
+ }
+ return jdbcUserRepository.save(user);
+
+ }
+
+ @Override
+ public User updateUser(User newUser) throws DataNotFoundException {
+ jdbcUserRepository.getById(newUser.getId()); //если id не правильный, то после этого метода вернётся нужная ошибка
+
+ return jdbcUserRepository.update(newUser);
+
+
+ }
+
+ @Override
+ public void addFriend(int id, int friendId) throws DataNotFoundException {
+ jdbcUserRepository.getById(id);
+ jdbcUserRepository.getById(friendId);
+
+ jdbcUserRepository.addFriend(id, friendId);
+ }
+
+ @Override
+ public void deleteFriend(int id, int friendId) throws DataNotFoundException {
+ jdbcUserRepository.getById(id);
+ jdbcUserRepository.getById(friendId);
+ jdbcUserRepository.deleteFriend(id, friendId);
+ }
+
+ @Override
+ public List getUserFriends(int id) throws DataNotFoundException {
+ jdbcUserRepository.getById(id);
+ return jdbcUserRepository.getUserFriends(id);
+
+ }
+
+ @Override
+ public List getCommonFriends(int id, int otherId) throws DataNotFoundException {
+ List userList = getUserFriends(id);
+ return getUserFriends(otherId).stream()
+ .filter(userList::contains)
+ .collect(Collectors.toList());
+
+
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/FilmStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/FilmStorage.java
index 4d0311d..059df2e 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/storage/FilmStorage.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/FilmStorage.java
@@ -16,4 +16,4 @@ public interface FilmStorage {
Film updateFilm(Film newFilm) throws DataNotFoundException;
Film get(int filmId) throws DataNotFoundException;
-}
+}
\ No newline at end of file
diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryFilmStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryFilmStorage.java
index b44b130..8ae9c80 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryFilmStorage.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryFilmStorage.java
@@ -69,4 +69,4 @@ private int getNextId() {
return nextId++;
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryUserStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryUserStorage.java
index 11bbd05..e7ce22f 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryUserStorage.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryUserStorage.java
@@ -67,4 +67,4 @@ private int getNextId() {
return nextId++;
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/UserStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/UserStorage.java
index 860a424..afcb15d 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/storage/UserStorage.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/UserStorage.java
@@ -14,4 +14,4 @@ public interface UserStorage {
User updateUser(User newUser) throws DataNotFoundException;
User get(int id) throws DataNotFoundException;
-}
+}
\ No newline at end of file
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 8b13789..42020f6 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1 +1,8 @@
+spring.sql.init.mode=always
+spring.datasource.url=jdbc:h2:file:./db/filmorate
+spring.datasource.driverClassName=org.h2.Driver
+spring.datasource.username=sa
+spring.datasource.password=password
+spring.h2.console.enabled=true
+server.servlet.encoding.charset=UTF-8
\ No newline at end of file
diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql
new file mode 100644
index 0000000..671c878
--- /dev/null
+++ b/src/main/resources/data.sql
@@ -0,0 +1,22 @@
+merge into mpa(mpa_id, mpa_name)
+values(1,'G');
+merge into mpa(mpa_id, mpa_name)
+values(2,'PG');
+merge into mpa(mpa_id, mpa_name)
+values(3,'PG-13');
+merge into mpa(mpa_id, mpa_name)
+values(4,'R');
+merge into mpa(mpa_id, mpa_name)
+values(5,'NC-17');
+merge into genres(genre_id, genre_name)
+values(1,'Комедия');
+merge into genres(genre_id, genre_name)
+values(2,'Драма');
+merge into genres(genre_id, genre_name)
+values(3,'Мультфильм');
+merge into genres(genre_id, genre_name)
+values(4,'Триллер');
+merge into genres(genre_id, genre_name)
+values(5,'Документальный');
+merge into genres(genre_id, genre_name)
+values(6,'Боевик');
\ No newline at end of file
diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql
new file mode 100644
index 0000000..f8e2400
--- /dev/null
+++ b/src/main/resources/schema.sql
@@ -0,0 +1,61 @@
+
+
+CREATE TABLE IF NOT EXISTS users(
+ user_id int generated by default as identity primary key,
+ login varchar(255) not null,
+ email varchar(255) not null,
+ user_name varchar(255),
+ birthday date not null
+);
+
+
+
+CREATE TABLE IF NOT EXISTS mpa (
+ mpa_id int generated by default as identity primary key,
+ mpa_name varchar(255) not NULL unique
+);
+
+CREATE TABLE IF NOT EXISTS films (
+ film_id int generated by default as identity primary key,
+ name varchar(255) not null,
+ description varchar(255) not null,
+ release_date date not null,
+ duration int not null,
+ mpa_id int not NULL,
+ FOREIGN KEY (mpa_id) REFERENCES mpa (mpa_id)
+);
+
+
+CREATE TABLE IF NOT EXISTS friends
+(
+ user_id int,
+ friend_id int,
+ foreign key (user_id) references users (user_id) on delete cascade,
+ foreign key (friend_id) references users (user_id) on delete cascade,
+ primary key (user_id, friend_id)
+);
+
+
+
+CREATE TABLE IF NOT EXISTS genres (
+ genre_id int generated by default as identity primary key,
+ genre_name varchar(255) not null unique
+);
+
+CREATE TABLE IF NOT EXISTS film_genres (
+ film_id int,
+ genre_id int,
+ foreign key (film_id) references films (film_id) on delete cascade,
+ foreign key (genre_id) references genres (genre_id) on delete cascade,
+ primary key (film_id, genre_id)
+);
+
+CREATE TABLE IF NOT EXISTS film_likes (
+ user_id int,
+ film_id int,
+ foreign key (film_id) references films (film_id) on delete cascade,
+ foreign key (user_id) references users (user_id) on delete cascade,
+ primary key (film_id, user_id)
+);
+
+
diff --git a/src/test/java/ru/yandex/practicum/filmorate/controller/FilmControllerTest.java b/src/test/java/ru/yandex/practicum/filmorate/controller/FilmControllerTest.java
deleted file mode 100644
index a8e13d9..0000000
--- a/src/test/java/ru/yandex/practicum/filmorate/controller/FilmControllerTest.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package ru.yandex.practicum.filmorate.controller;
-
-import org.junit.jupiter.api.Test;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.context.SpringBootTest;
-import ru.yandex.practicum.filmorate.exception.DataNotFoundException;
-import ru.yandex.practicum.filmorate.exception.ValidationException;
-import ru.yandex.practicum.filmorate.model.Film;
-
-import java.time.LocalDate;
-import java.util.Collection;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-@SpringBootTest
-public class FilmControllerTest {
-
- @Autowired
- private FilmController filmController;
-
- @Test
- public void returnFilmsTest() throws ValidationException {
- Film film1 = new Film();
- film1.setName("filmName1");
- film1.setDescription("Descr1");
- film1.setReleaseDate(LocalDate.of(2023, 12, 15));
- film1.setDuration(168L);
-
- Film film2 = new Film();
- film2.setName("filmName2");
- film2.setDescription("Descr2");
- film2.setReleaseDate(LocalDate.of(2023, 12, 15));
- film2.setDuration(168L);
-
- filmController.createFilm(film1);
- filmController.createFilm(film2);
-
- Collection films = filmController.getFilms();
-
- assertTrue(films.contains(film1));
- assertTrue(films.contains(film2));
- }
-
- @Test
- void filmsValidTest() throws ValidationException {
- Film film = new Film();
- film.setName("filmName1");
- film.setDescription("Descr1");
- film.setReleaseDate(LocalDate.of(2023, 12, 15));
- film.setDuration(168L);
-
- Film savedFilm = filmController.createFilm(film);
-
- assertNotNull(savedFilm.getId());
- assertEquals("filmName1", savedFilm.getName());
- assertEquals("Descr1", savedFilm.getDescription());
- assertEquals(LocalDate.of(2023, 12, 15), savedFilm.getReleaseDate());
- assertEquals(168L, savedFilm.getDuration());
-
- film.setName(null);
- assertThrows(ValidationException.class, () -> filmController.createFilm(film));
-
- film.setName("filmName1");
- film.setDescription("1111111111111111111111111111111111111111111111111111111111111111111" + "111111111111111111111111111111111111111111111111111111111111111111111111111111111" + "111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111");
- assertThrows(ValidationException.class, () -> filmController.createFilm(film));
-
- film.setDescription("Descr1");
- film.setReleaseDate(LocalDate.of(1894, 12, 15));
- assertThrows(ValidationException.class, () -> filmController.createFilm(film));
-
- film.setReleaseDate(LocalDate.of(2023, 12, 15));
- film.setDuration(-1L);
- assertThrows(ValidationException.class, () -> filmController.createFilm(film));
- }
-
- @Test
- void updateFilmTest() throws ValidationException {
- Film film = new Film();
- film.setName("filmName1");
- film.setDescription("Descr1");
- film.setReleaseDate(LocalDate.of(2023, 12, 15));
- film.setDuration(168L);
-
- filmController.createFilm(film);
-
- Film filmUpdate = new Film();
- filmUpdate.setId(8);
- assertThrows(DataNotFoundException.class, () -> filmController.updateFilm(filmUpdate));
- }
-}
diff --git a/src/test/java/ru/yandex/practicum/filmorate/controller/JdbcUserRopositoryTest.java b/src/test/java/ru/yandex/practicum/filmorate/controller/JdbcUserRopositoryTest.java
new file mode 100644
index 0000000..a26238b
--- /dev/null
+++ b/src/test/java/ru/yandex/practicum/filmorate/controller/JdbcUserRopositoryTest.java
@@ -0,0 +1,46 @@
+package ru.yandex.practicum.filmorate.controller;
+
+import lombok.RequiredArgsConstructor;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest;
+import org.springframework.context.annotation.Import;
+import ru.yandex.practicum.filmorate.model.User;
+import ru.yandex.practicum.filmorate.repository.JdbcUserRepository;
+import ru.yandex.practicum.filmorate.repository.mappers.UserRowMapper;
+
+import java.time.LocalDate;
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@JdbcTest
+@Import({JdbcUserRepository.class, UserRowMapper.class})
+@RequiredArgsConstructor(onConstructor_ = @Autowired)
+public class JdbcUserRopositoryTest {
+ public static final int TEST_USER_ID = 1;
+ private final JdbcUserRepository jdbcUserRepository;
+ private final UserRowMapper mapper;
+
+ static User getTestUser() {
+ User user = new User();
+ user.setId(TEST_USER_ID);
+ user.setLogin("userLogin");
+ user.setEmail("email@email.com");
+ user.setName("user");
+ user.setBirthday(LocalDate.of(2000, 3, 22));
+ return user;
+ }
+
+ @Test
+ public void find_by_id() {
+ Optional userOptional = jdbcUserRepository.getById(TEST_USER_ID);
+
+ assertThat(userOptional)
+ .isPresent()
+ .get()
+ .usingRecursiveComparison()
+ .ignoringExpectedNullFields()
+ .isEqualTo(getTestUser());
+ }
+}
diff --git a/src/test/java/ru/yandex/practicum/filmorate/controller/UserControllerTest.java b/src/test/java/ru/yandex/practicum/filmorate/controller/UserControllerTest.java
deleted file mode 100644
index dbf950f..0000000
--- a/src/test/java/ru/yandex/practicum/filmorate/controller/UserControllerTest.java
+++ /dev/null
@@ -1,100 +0,0 @@
-package ru.yandex.practicum.filmorate.controller;
-
-import org.junit.jupiter.api.Test;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.context.SpringBootTest;
-import ru.yandex.practicum.filmorate.exception.DataNotFoundException;
-import ru.yandex.practicum.filmorate.exception.ValidationException;
-import ru.yandex.practicum.filmorate.model.User;
-
-import java.time.LocalDate;
-import java.util.Collection;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-
-@SpringBootTest
-public class UserControllerTest {
-
-
- @Autowired
- private UserController userController;
-
- @Test
- public void returnsUsersTest() throws ValidationException {
- User user1 = new User();
- user1.setEmail("user1@yandex.ru");
- user1.setLogin("user1");
- user1.setName("User1");
- user1.setBirthday(LocalDate.of(1999, 8, 6));
-
- User user2 = new User();
- user2.setEmail("user2@yandex.ru");
- user2.setLogin("user2");
- user2.setName("User2");
- user2.setBirthday(LocalDate.of(1991, 3, 20));
-
- userController.createUser(user1);
- userController.createUser(user2);
-
- Collection users = userController.getUsers();
-
- assertTrue(users.contains(user1));
- assertTrue(users.contains(user2));
- }
-
- @Test
- void userValidTest() throws ValidationException {
- User user = new User();
- user.setEmail("user1@yandex.ru");
- user.setLogin("user1");
- user.setName("user1");
- user.setBirthday(LocalDate.of(1999, 8, 6));
-
- User savedUser1 = userController.createUser(user);
-
- assertNotNull(savedUser1.getId());
- assertEquals("user1@yandex.ru", savedUser1.getEmail());
- assertEquals("user1", savedUser1.getLogin());
- assertEquals("user1", savedUser1.getName());
- assertEquals(LocalDate.of(1999, 8, 6), savedUser1.getBirthday());
-
- user.setEmail("12345");
- assertThrows(ValidationException.class, () -> userController.createUser(user));
-
- user.setEmail("user3.com");
- assertThrows(ValidationException.class, () -> userController.createUser(user));
-
- user.setEmail("user1@yandex.ru");
- user.setLogin("");
- assertThrows(ValidationException.class, () -> userController.createUser(user));
-
- user.setLogin(null);
- assertThrows(ValidationException.class, () -> userController.createUser(user));
-
- user.setLogin("user 1");
- assertThrows(ValidationException.class, () -> userController.createUser(user));
-
- user.setBirthday(LocalDate.now().plusMonths(5));
- assertThrows(ValidationException.class, () -> userController.createUser(user));
- }
-
- @Test
- void updateUserTest() throws ValidationException {
- User user1 = new User();
- user1.setEmail("user1@yandex.ru");
- user1.setLogin("user1");
- user1.setName(null);
- user1.setBirthday(LocalDate.of(1999, 8, 6));
-
- userController.createUser(user1);
-
- User userUpdate = new User();
- userUpdate.setEmail("user1@yandex.ru");
- userUpdate.setLogin("user1");
- userUpdate.setName(null);
- userUpdate.setBirthday(LocalDate.of(1999, 8, 6));
- userUpdate.setId(8);
- assertThrows(DataNotFoundException.class, () -> userController.updateUser(userUpdate));
- }
-}
diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties
new file mode 100644
index 0000000..c27dcec
--- /dev/null
+++ b/src/test/resources/application.properties
@@ -0,0 +1,8 @@
+spring.sql.init.mode=always
+spring.datasource.url=jdbc:h2:mem:filmorate
+spring.datasource.driverClassName=org.h2.Driver
+spring.datasource.username=sa
+spring.datasource.password=password
+spring.sql.init.data-locations=classpath:test-data.sql
+spring.h2.console.enabled=true
+server.servlet.encoding.charset=UTF-8
\ No newline at end of file
diff --git a/src/test/resources/test-data.sql b/src/test/resources/test-data.sql
new file mode 100644
index 0000000..e65fef3
--- /dev/null
+++ b/src/test/resources/test-data.sql
@@ -0,0 +1 @@
+INSERT INTO USERS (LOGIN, EMAIL, USER_NAME, BIRTHDAY) VALUES('userLogin', 'email@email.com', 'user', '2000-3-22');
\ No newline at end of file