Configure pirsistance, dao, services and controllers

This commit is contained in:
Артем Харламов 2024-11-28 00:41:38 +04:00
parent 2c01307bd0
commit 93e11b8537
27 changed files with 803 additions and 0 deletions

View File

@ -19,6 +19,9 @@ repositories {
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation group: 'org.springdoc', name: 'springdoc-openapi-starter-webmvc-ui', version: '2.6.0'
runtimeOnly 'com.h2database:h2'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

BIN
network/data/home.mv.db Normal file

Binary file not shown.

View File

@ -0,0 +1,3 @@
2024-11-25 15:24:18.380541+04:00 jdbc[3]: exception
org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "AUTHOR" not found (this database is empty); SQL statement:
insert into author (email,first_name,last_name,password,id) values (?,?,?,?,default) [42104-224]

View File

@ -0,0 +1,40 @@
package com.ulstu.network.advice;
import com.ulstu.network.exceptions.AuthorNotFoundException;
import com.ulstu.network.exceptions.BookNotFoundException;
import com.ulstu.network.exceptions.ValidationException;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import java.util.stream.Collectors;
@ControllerAdvice
public class AdviceController {
@ExceptionHandler({
AuthorNotFoundException.class,
BookNotFoundException.class,
ValidationException.class
})
public ResponseEntity<Object> handleException(Throwable e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Object> handleBindException(MethodArgumentNotValidException e) {
final ValidationException validationException = new ValidationException(
e.getBindingResult().getAllErrors().stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.collect(Collectors.toSet()));
return handleException(validationException);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<Object> handleUnknownException(Throwable e) {
e.printStackTrace();
return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}

View File

@ -0,0 +1,54 @@
package com.ulstu.network.controllers;
import com.ulstu.network.dto.AuthorDTO;
import com.ulstu.network.models.Author;
import com.ulstu.network.services.AuthorService;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/author")
public class AuthorController {
private final AuthorService authorService;
public AuthorController(AuthorService authorService){
this.authorService = authorService;
}
@GetMapping("/{id}")
public AuthorDTO getAuthor(@PathVariable Long id) {
return new AuthorDTO(authorService.findAuthor(id));
}
@GetMapping
public List<AuthorDTO> getAuthors() {
return authorService.findAllAuthors().stream()
.map(AuthorDTO::new)
.toList();
}
@PostMapping
public AuthorDTO createAuthor(@RequestBody @Valid Author author) {
return new AuthorDTO(authorService.addAuthor(author));
}
@PostMapping("/{id}")
public AuthorDTO attachBookToAuthor(@PathVariable Long id, @RequestParam Long bookId) {
return new AuthorDTO(authorService.attachBookToAuthor(id, bookId));
}
@PutMapping("/{id}")
public AuthorDTO updateAuthor(@PathVariable Long id, @RequestBody @Valid Author author ){
return new AuthorDTO(authorService.updateAuthor(id, author));
}
@DeleteMapping("/{id}")
public void deleteAuthor(@PathVariable Long id){
authorService.deleteAuthor(id);
}
}

View File

@ -0,0 +1,42 @@
package com.ulstu.network.controllers;
import com.ulstu.network.dto.BookDTO;
import com.ulstu.network.dto.BookDataDTO;
import com.ulstu.network.models.Book;
import com.ulstu.network.services.BookService;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/book")
public class BookController {
private final BookService bookService;
public BookController(BookService bookService) { this.bookService = bookService; }
@GetMapping("/{id}")
public BookDTO getBook(@PathVariable long id) { return new BookDTO(bookService.findBook(id)); }
@GetMapping
public List<BookDTO> getBooks() {
return bookService.findAllBooks().stream()
.map(BookDTO::new)
.toList();
}
@PostMapping
public BookDTO createBook(@RequestBody @Valid BookDataDTO bookData) {
return new BookDTO(bookService.addBook(bookData));
}
@PutMapping("/{id}")
public BookDTO updateBook(@PathVariable Long id, @RequestBody @Valid BookDataDTO bookData) {
return new BookDTO(bookService.updateBook(id, bookData));
}
@DeleteMapping("/{id}")
public void deleteBook(@PathVariable Long id) {bookService.deleteBook(id);}
}

View File

@ -0,0 +1,34 @@
package com.ulstu.network.dto;
import com.ulstu.network.models.Author;
import com.ulstu.network.models.Book;
import java.util.List;
public class AuthorDTO {
private final long id;
private final String name;
private final String email;
private final List<Book> books;
public AuthorDTO(Author author) {
this.id = author.getId();
this.name = String.format("%s %s", author.getFirstName(), author.getLastName());
this.email = author.getEmail();
this.books = author.getBooks();
}
public long getId() {
return id;
}
public String getName() {
return name;
}
public String getEmail() {
return email;
}
public List<Book> getBooks(){
return books;
}
}

View File

@ -0,0 +1,25 @@
package com.ulstu.network.dto;
import com.ulstu.network.models.Author;
import com.ulstu.network.models.Book;
import java.util.List;
public class BookDTO {
private final long id;
private final String title;
private final String isbn;
private final List<Author> authors;
public BookDTO(Book book) {
this.id = book.getId();
this.title = book.getTitle();
this.isbn = book.getIsbn();
this.authors = book.getAuthors();
}
public long getId() {return id;}
public String getTitle() {return title;}
public String getIsbn() {return isbn;}
public List<Author> getAuthors() {return authors;}
}

View File

@ -0,0 +1,39 @@
package com.ulstu.network.dto;
import jakarta.validation.constraints.NotBlank;
import java.util.List;
public class BookDataDTO {
@NotBlank(message = "Title can't be empty")
private String title;
@NotBlank(message = "ISBN can't be empty")
private String isbn;
private List<Long> authors;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getIsbn() {
return isbn;
}
public void setIsbn(String isbn) {
this.isbn = isbn;
}
public List<Long> getAuthors() {
return authors;
}
public void setAuthors(List<Long> authors) {
this.authors = authors;
}
}

View File

@ -0,0 +1,4 @@
package com.ulstu.network.dto;
public class LibraryDTO {
}

View File

@ -0,0 +1,7 @@
package com.ulstu.network.exceptions;
public class AuthorNotFoundException extends RuntimeException{
public AuthorNotFoundException(Long id){
super(String.format("Author with id [%s] is not found", id));
}
}

View File

@ -0,0 +1,8 @@
package com.ulstu.network.exceptions;
public class BookNotFoundException extends RuntimeException{
public BookNotFoundException(Long id){
super(String.format("Book with id [%s] is not found", id));
}
}

View File

@ -0,0 +1,9 @@
package com.ulstu.network.exceptions;
import java.util.Set;
public class ValidationException extends RuntimeException {
public <T> ValidationException(Set<String> errors) {
super(String.join("\n", errors));
}
}

View File

@ -0,0 +1,85 @@
package com.ulstu.network.models;
import jakarta.persistence.*;
import jakarta.validation.constraints.*;
import java.util.List;
@Entity
public class Author {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(nullable = false)
@NotBlank(message = "First Name can't be empty")
private String firstName;
@Column(nullable = false)
@NotBlank(message = "Last Name can't be empty")
private String lastName;
@Email(message = "Please enter a correct email address")
@NotBlank(message = "Email can't be empty")
private String email;
/*@Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\\\d).{8}$",
message = "Password is not valid")*/
@NotBlank
private String password;
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "book_author", joinColumns = @JoinColumn(name = "book_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "author_id", referencedColumnName = "id"))
private List<Book> books;
public Author() {
}
public Author(String firstName, String lastName, String email, String password) {
super();
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.password = password;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public void setEmail (String email) { this.email = email; }
public String getEmail () { return email; }
public void setPassword (String password) { this.password = password; }
public String getPassword () { return password; }
public List<Book> getBooks() {
return books;
}
public void setBooks(List<Book> books) {
this.books = books;
}
}

View File

@ -0,0 +1,64 @@
package com.ulstu.network.models;
import jakarta.persistence.*;
import java.util.List;
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(nullable = false)
private String title;
@Column(nullable = false)
private String isbn;
@ManyToMany(mappedBy = "books", fetch = FetchType.EAGER)
private List<Author> authors;
public Book() {
}
public Book(String title, String isbn) {
super();
this.title = title;
this.isbn = isbn;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getIsbn() {
return isbn;
}
public void setIsbn(String isbn) {
this.isbn = isbn;
}
public List<Author> getAuthors() {
return authors;
}
public void setAuthors(List<Author> authors) {
this.authors = authors;
}
}

View File

@ -0,0 +1,66 @@
package com.ulstu.network.models;
import jakarta.persistence.*;
import java.util.List;
@Entity
public class Library {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column
private String name;
@Column
private String address;
@OneToMany
@JoinTable(name = "books_in_library",
joinColumns = @JoinColumn(name = "library_id"),
inverseJoinColumns = @JoinColumn(name = "book_id"))
private List<Book> books;
public Library() {
}
public Library(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public List<Book> getBooks() {
return books;
}
public void setBooks(List<Book> books) {
this.books = books;
}
}

View File

@ -0,0 +1,69 @@
package com.ulstu.network.models;
import jakarta.persistence.*;
import java.util.List;
@Entity
@Table(name = "USERS")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column
private String name;
@Column
private String email;
@Column
private String password;
@ManyToMany(cascade = {CascadeType.ALL})
@JoinTable(name="user_books",
joinColumns=@JoinColumn(name="user_id", referencedColumnName = "id"),
inverseJoinColumns=@JoinColumn(name="book_id", referencedColumnName = "id"))
private List<Book> books;
public User(){
}
public User(String name, String email, String password){
super();
this.name = name;
this.email = email;
this.password = password;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public void setName (String name) { this.name = name; }
public String getName () { return name; }
public void setEmail (String email) { this.email = email; }
public String getEmail () { return email; }
public void setPassword (String password) { this.password = password; }
public String getPassword () { return password; }
public List<Book> getBooks() {
return books;
}
public void setBooks(List<Book> books) {
this.books = books;
}
public void addBook(Book book) { this.books.add(book); }
}

View File

@ -0,0 +1,6 @@
package com.ulstu.network.repositories;
import com.ulstu.network.models.Author;
import org.springframework.data.jpa.repository.JpaRepository;
public interface AuthorRepository extends JpaRepository<Author, Long> {}

View File

@ -0,0 +1,6 @@
package com.ulstu.network.repositories;
import com.ulstu.network.models.Book;
import org.springframework.data.jpa.repository.JpaRepository;
public interface BookRepository extends JpaRepository<Book, Long> {}

View File

@ -0,0 +1,11 @@
package com.ulstu.network.repositories;
import com.ulstu.network.models.Library;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
public interface LibraryRepository extends JpaRepository<Library, Long> {}

View File

@ -0,0 +1,7 @@
package com.ulstu.network.repositories;
import com.ulstu.network.models.User;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Long> {
}

View File

@ -0,0 +1,73 @@
package com.ulstu.network.services;
import com.ulstu.network.models.Book;
import com.ulstu.network.repositories.BookRepository;
import com.ulstu.network.validation.ValidatorUtil;
import com.ulstu.network.exceptions.AuthorNotFoundException;
import com.ulstu.network.models.Author;
import com.ulstu.network.repositories.AuthorRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class AuthorService {
private final AuthorRepository authorRepository;
private final BookService bookService;
private final ValidatorUtil validatorUtil;
public AuthorService(AuthorRepository authorRepository, BookService bookService, ValidatorUtil validatorUtil){
this.authorRepository = authorRepository;
this.bookService = bookService;
this.validatorUtil = validatorUtil;
}
@Transactional(readOnly = true)
public Author findAuthor(Long id) {
return authorRepository.findById(id).orElseThrow(()->new AuthorNotFoundException(id));
}
@Transactional(readOnly = true)
public List<Author> findAllAuthors() {
return authorRepository.findAll();
}
@Transactional
public Author addAuthor(Author author) {
validatorUtil.validate(author);
return authorRepository.save(author);
}
@Transactional
public Author attachBookToAuthor(Long authorId, Long bookId) {
final Author currentAuthor = findAuthor(authorId);
currentAuthor.getBooks().add(bookService.findBook(bookId));
return authorRepository.save(currentAuthor);
}
@Transactional
public Author updateAuthor(Long id, Author author) {
validatorUtil.validate(author);
final Author currentAuthor = findAuthor(id);
currentAuthor.setFirstName(author.getFirstName());
currentAuthor.setLastName(author.getLastName());
currentAuthor.setEmail(author.getEmail());
currentAuthor.setPassword(author.getPassword());
return authorRepository.save(currentAuthor);
}
@Transactional
public void deleteAuthor(Long id) {
final Author currentAuthor = findAuthor(id);
authorRepository.delete(currentAuthor);
}
@Transactional
public void deleteAllAuthors() {
authorRepository.deleteAll();
}
}

View File

@ -0,0 +1,85 @@
package com.ulstu.network.services;
import com.ulstu.network.dto.BookDataDTO;
import com.ulstu.network.exceptions.AuthorNotFoundException;
import com.ulstu.network.exceptions.BookNotFoundException;
import com.ulstu.network.models.Author;
import com.ulstu.network.models.Book;
import com.ulstu.network.repositories.AuthorRepository;
import com.ulstu.network.repositories.BookRepository;
import com.ulstu.network.validation.ValidatorUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static java.util.Collections.emptyList;
@Service
public class BookService {
private final BookRepository bookRepository;
private final AuthorRepository authorRepository;
private final ValidatorUtil validatorUtil;
public BookService(BookRepository bookRepository, AuthorRepository authorRepository, ValidatorUtil validatorUtil){
this.bookRepository = bookRepository;
this.authorRepository = authorRepository;
this.validatorUtil = validatorUtil;
}
@Transactional(readOnly = true)
public Book findBook(Long id) {
return bookRepository.findById(id).orElseThrow(()->new BookNotFoundException(id));
}
@Transactional(readOnly = true)
public List<Book> findAllBooks() {
return bookRepository.findAll();
}
@Transactional
public Book addBook(BookDataDTO bookData) {
validatorUtil.validate(bookData);
Book newBook = new Book(bookData.getTitle(), bookData.getIsbn());
List<Author> authors = new ArrayList<>();
for (long id : bookData.getAuthors())
{
authorRepository.findById(id).ifPresent(authors::add);
}
newBook.setAuthors(authors);
return bookRepository.save(newBook);
}
@Transactional
public Book updateBook(Long id, BookDataDTO bookData) {
validatorUtil.validate(bookData);
final Book currentBook = findBook(id);
currentBook.setTitle(bookData.getTitle());
currentBook.setIsbn(bookData.getIsbn());
List<Author> authors = emptyList();
for (long auth : bookData.getAuthors())
{
authorRepository.findById(auth).ifPresent(authors::add);
}
currentBook.setAuthors(authors);
return bookRepository.save(currentBook);
}
@Transactional
public void deleteBook(Long id) {
final Book currentBook = findBook(id);
bookRepository.delete(currentBook);
}
public void deleteAllBooks(){
bookRepository.deleteAll();
}
}

View File

@ -0,0 +1,4 @@
package com.ulstu.network.services;
public class LibraryService {
}

View File

@ -0,0 +1,24 @@
/*
package com.ulstu.network.swagger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.example.controller")) // Replace with your controller package
.paths(PathSelectors.any())
.build();
}
}
*/

View File

@ -0,0 +1,26 @@
package com.ulstu.network.validation;
import com.ulstu.network.exceptions.ValidationException;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import org.springframework.stereotype.Component;
import java.util.Set;
import java.util.stream.Collectors;
@Component
public class ValidatorUtil {
private final Validator validator;
public ValidatorUtil() {
this.validator = Validation.buildDefaultValidatorFactory().getValidator();
}
public <T> void validate(T object) {
final Set<ConstraintViolation<T>> errors = validator.validate(object);
if (!errors.isEmpty()) {
throw new ValidationException(errors.stream()
.map(ConstraintViolation::getMessage)
.collect(Collectors.toSet()));
}
}
}

View File

@ -1 +1,10 @@
spring.application.name=network
# H2 Database
spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:file:C://tmp/data
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.hibernate.ddl-auto=update
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect