diff --git a/pom.xml b/pom.xml
index ffbd96b..59f6b0b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2,49 +2,40 @@
4.0.0
+
org.springframework.boot
spring-boot-starter-parent
3.4.1
+
org.example
SpringBoot-BookShop
0.0.1-SNAPSHOT
SpringBoot-BookShop
SpringBoot-BookShop
-
-
-
-
-
-
-
-
-
-
-
-
-
+
17
checkstyle.xml
+
org.springframework.boot
- spring-boot-starter
+ spring-boot-starter-web
org.springframework.boot
- spring-boot-starter-test
- test
+ spring-boot-starter-data-jpa
org.springframework.boot
- spring-boot-starter-data-jpa
+ spring-boot-starter-test
+ test
@@ -64,39 +55,58 @@
- org.springframework.boot
- spring-boot-starter-web
+ org.mapstruct
+ mapstruct
+ 1.6.3
+
+
+
+ org.liquibase
+ liquibase-core
+ 4.23.0
-
- org.springframework.boot
- spring-boot-maven-plugin
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.13.0
+
+ 17
+ 17
+
+
+ org.projectlombok
+ lombok
+ 1.18.36
+
+
+ org.mapstruct
+ mapstruct-processor
+ 1.6.3
+
+
+
- org.apache.maven.plugins
- maven-checkstyle-plugin
- 3.3.0
+ org.liquibase
+ liquibase-maven-plugin
+ 4.23.0
- compile
+ process-resources
+
+ src/main/resources/liquibase.properties
+
- check
+ update
-
- ${maven.checkstyle.plugin.configLocation}
- true
- true
- false
-
-
diff --git a/src/main/java/org/example/SpringBootBookShopApplication.java b/src/main/java/org/example/SpringBootBookShopApplication.java
index 48c1e9f..d7dc995 100644
--- a/src/main/java/org/example/SpringBootBookShopApplication.java
+++ b/src/main/java/org/example/SpringBootBookShopApplication.java
@@ -1,9 +1,7 @@
package org.example;
-import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class SpringBootBookShopApplication {
@@ -11,14 +9,4 @@ public class SpringBootBookShopApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootBookShopApplication.class, args);
}
- @Bean
- public CommandLineRunner commandLineRunner() {
- return new CommandLineRunner() {
- @Override
- public void run(String... args) throws Exception {
-
- }
- };
- }
-
}
diff --git a/src/main/java/org/example/config/MapperConfig.java b/src/main/java/org/example/config/MapperConfig.java
new file mode 100644
index 0000000..0fd0054
--- /dev/null
+++ b/src/main/java/org/example/config/MapperConfig.java
@@ -0,0 +1,13 @@
+package org.example.config;
+
+import org.mapstruct.InjectionStrategy;
+import org.mapstruct.NullValueCheckStrategy;
+
+@org.mapstruct.MapperConfig(
+ componentModel = "spring",
+ injectionStrategy = InjectionStrategy.CONSTRUCTOR,
+ nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS,
+ implementationPackage = ".impl"
+)
+public class MapperConfig {
+}
diff --git a/src/main/java/org/example/controller/BookController.java b/src/main/java/org/example/controller/BookController.java
new file mode 100644
index 0000000..2f4cf54
--- /dev/null
+++ b/src/main/java/org/example/controller/BookController.java
@@ -0,0 +1,43 @@
+package org.example.controller;
+
+import lombok.RequiredArgsConstructor;
+import org.example.dto.BookDto;
+import org.example.dto.CreateBookRequestDto;
+import org.example.service.BookService;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.*;
+import java.util.List;
+
+@RequiredArgsConstructor
+@RestController
+@RequestMapping(value = "/books")
+public class BookController {
+
+ private final BookService bookService;
+
+ @GetMapping
+ public List getAll() {
+ return bookService.findAll();
+ }
+
+ @GetMapping("/{id}")
+ public BookDto getBookById(@PathVariable Long id) {
+ return bookService.findById(id);
+ }
+
+ @PostMapping
+ public BookDto createBook(@RequestBody CreateBookRequestDto createBookRequestDto) {
+ return bookService.save(createBookRequestDto);
+ }
+
+ @ResponseStatus(HttpStatus.NO_CONTENT)
+ @DeleteMapping("/{id}")
+ public void deleteBookById(@PathVariable Long id) {
+ bookService.deleteById(id);
+ }
+
+ @PutMapping("/{id}")
+ public void updateBookById(@PathVariable Long id, @RequestBody BookDto bookDto) {
+ bookService.updateBookById(id, bookDto);
+ }
+}
diff --git a/src/main/java/org/example/dto/BookDto.java b/src/main/java/org/example/dto/BookDto.java
new file mode 100644
index 0000000..3a2bf19
--- /dev/null
+++ b/src/main/java/org/example/dto/BookDto.java
@@ -0,0 +1,15 @@
+package org.example.dto;
+
+import lombok.Data;
+import java.math.BigDecimal;
+
+@Data
+public class BookDto {
+ private Long id;
+ private String title;
+ private String author;
+ private String isbn;
+ private BigDecimal price;
+ private String description;
+ private String coverImage;
+}
diff --git a/src/main/java/org/example/dto/CreateBookRequestDto.java b/src/main/java/org/example/dto/CreateBookRequestDto.java
new file mode 100644
index 0000000..e3da19c
--- /dev/null
+++ b/src/main/java/org/example/dto/CreateBookRequestDto.java
@@ -0,0 +1,14 @@
+package org.example.dto;
+
+import lombok.Data;
+import java.math.BigDecimal;
+
+@Data
+public class CreateBookRequestDto {
+ private String title;
+ private String author;
+ private String isbn;
+ private BigDecimal price;
+ private String description;
+ private String coverImage;
+}
diff --git a/src/main/java/org/example/exception/BookNotFoundException.java b/src/main/java/org/example/exception/BookNotFoundException.java
new file mode 100644
index 0000000..aca5f3e
--- /dev/null
+++ b/src/main/java/org/example/exception/BookNotFoundException.java
@@ -0,0 +1,7 @@
+package org.example.exception;
+
+public class BookNotFoundException extends RuntimeException {
+ public BookNotFoundException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/org/example/exception/BookSaveException.java b/src/main/java/org/example/exception/BookSaveException.java
new file mode 100644
index 0000000..07a9142
--- /dev/null
+++ b/src/main/java/org/example/exception/BookSaveException.java
@@ -0,0 +1,7 @@
+package org.example.exception;
+
+public class BookSaveException extends RuntimeException {
+ public BookSaveException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/main/java/org/example/exception/BookValidationException.java b/src/main/java/org/example/exception/BookValidationException.java
new file mode 100644
index 0000000..1ac75ff
--- /dev/null
+++ b/src/main/java/org/example/exception/BookValidationException.java
@@ -0,0 +1,7 @@
+package org.example.exception;
+
+public class BookValidationException extends RuntimeException {
+ public BookValidationException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/org/example/exception/EntityNotFoundException.java b/src/main/java/org/example/exception/EntityNotFoundException.java
new file mode 100644
index 0000000..ac0627d
--- /dev/null
+++ b/src/main/java/org/example/exception/EntityNotFoundException.java
@@ -0,0 +1,7 @@
+package org.example.exception;
+
+public class EntityNotFoundException extends RuntimeException {
+ public EntityNotFoundException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/org/example/mapper/BookMapper.java b/src/main/java/org/example/mapper/BookMapper.java
new file mode 100644
index 0000000..a91858a
--- /dev/null
+++ b/src/main/java/org/example/mapper/BookMapper.java
@@ -0,0 +1,13 @@
+package org.example.mapper;
+
+import org.example.config.MapperConfig;
+import org.example.dto.BookDto;
+import org.example.dto.CreateBookRequestDto;
+import org.example.model.Book;
+import org.mapstruct.Mapper;
+
+@Mapper(config = MapperConfig.class)
+public interface BookMapper {
+ BookDto toDto(Book book);
+ Book toModel(CreateBookRequestDto createBookRequestDto);
+}
diff --git a/src/main/java/org/example/model/Book.java b/src/main/java/org/example/model/Book.java
index dd2094f..f636aae 100644
--- a/src/main/java/org/example/model/Book.java
+++ b/src/main/java/org/example/model/Book.java
@@ -1,4 +1,37 @@
package org.example.model;
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
+import jakarta.persistence.Column;
+import lombok.Data;
+import org.hibernate.annotations.SQLDelete;
+import org.hibernate.annotations.Where;
+
+import java.math.BigDecimal;
+
+@Entity
+@Data
+@SQLDelete(sql = "UPDATE books SET is_deleted = TRUE WHERE id=?")
+@Where(clause = "is_deleted=false")
+@Table(name = "books")
public class Book {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+ @Column(nullable = false)
+ private String title;
+ @Column(nullable = false)
+ private String author;
+ @Column(nullable = false, unique = true)
+ private String isbn;
+ @Column(nullable = false)
+ private BigDecimal price;
+ private String description;
+ private String coverImage;
+ @Column(nullable = false)
+ private boolean isDeleted = false;
}
diff --git a/src/main/java/org/example/repository/BookRepository.java b/src/main/java/org/example/repository/BookRepository.java
index b23074b..5cb6f40 100644
--- a/src/main/java/org/example/repository/BookRepository.java
+++ b/src/main/java/org/example/repository/BookRepository.java
@@ -1,4 +1,22 @@
package org.example.repository;
-public interface BookRepository {
+import org.example.model.Book;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+
+import java.math.BigDecimal;
+
+public interface BookRepository extends JpaRepository {
+
+ @Modifying
+ @Query("UPDATE Book b SET "
+ + " b.title = :title,"
+ + " b.author = :author,"
+ + " b.isbn = :isbn,"
+ + " b.price = :price,"
+ + " b.description = :description,"
+ + " b.coverImage = :coverImage WHERE b.id = :id AND b.isDeleted = false")
+ void updateBookById(Long id, String title, String author, String isbn,
+ BigDecimal price, String description, String coverImage);
}
diff --git a/src/main/java/org/example/repository/impl/BookRepositoryImpl.java b/src/main/java/org/example/repository/impl/BookRepositoryImpl.java
deleted file mode 100644
index ef51b30..0000000
--- a/src/main/java/org/example/repository/impl/BookRepositoryImpl.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package org.example.repository.impl;
-
-public class BookRepositoryImpl {
-}
diff --git a/src/main/java/org/example/service/BookService.java b/src/main/java/org/example/service/BookService.java
index e8a91ce..872b954 100644
--- a/src/main/java/org/example/service/BookService.java
+++ b/src/main/java/org/example/service/BookService.java
@@ -1,4 +1,13 @@
package org.example.service;
+import org.example.dto.BookDto;
+import org.example.dto.CreateBookRequestDto;
+import java.util.List;
+
public interface BookService {
+ BookDto save(CreateBookRequestDto createBookRequestDto);
+ List findAll();
+ BookDto findById(Long id);
+ void deleteById(Long id);
+ void updateBookById(Long id, BookDto bookDto);
}
diff --git a/src/main/java/org/example/service/impl/BookServiceImpl.java b/src/main/java/org/example/service/impl/BookServiceImpl.java
index 66e8c38..6ab4785 100644
--- a/src/main/java/org/example/service/impl/BookServiceImpl.java
+++ b/src/main/java/org/example/service/impl/BookServiceImpl.java
@@ -1,4 +1,68 @@
package org.example.service.impl;
-public class BookServiceImpl {
+import lombok.RequiredArgsConstructor;
+import org.example.dto.BookDto;
+import org.example.dto.CreateBookRequestDto;
+import org.example.exception.BookNotFoundException;
+import org.example.exception.BookSaveException;
+import org.example.exception.BookValidationException;
+import org.example.mapper.BookMapper;
+import org.example.model.Book;
+import org.example.repository.BookRepository;
+import org.example.service.BookService;
+import org.springframework.stereotype.Service;
+import java.util.List;
+
+@Service
+@RequiredArgsConstructor
+public class BookServiceImpl implements BookService {
+ private final BookRepository bookRepository;
+ private final BookMapper bookMapper;
+
+ @Override
+ public BookDto save(CreateBookRequestDto createBookRequestDto) {
+ try {
+ if (createBookRequestDto.getTitle() == null) {
+ throw new BookValidationException("Title can't be null");
+ }
+
+ Book book = bookMapper.toModel(createBookRequestDto);
+ Book savedBook = bookRepository.save(book);
+
+ return bookMapper.toDto(savedBook);
+
+ } catch (Exception e) {
+ throw new BookSaveException("Can't save book", e);
+ }
+ }
+
+ @Override
+ public List findAll() {
+ return bookRepository.findAll().stream()
+ .map(bookMapper::toDto)
+ .toList();
+ }
+
+ @Override
+ public BookDto findById(Long id) {
+ Book book = bookRepository.findById(id)
+ .orElseThrow(() -> new BookNotFoundException("Book with id: " + id + " not found"));
+ return bookMapper.toDto(book);
+ }
+
+ @Override
+ public void deleteById(Long id) {
+ bookRepository.deleteById(id);
+ }
+
+ @Override
+ public void updateBookById(Long id, BookDto bookDto) {
+ if (!bookRepository.existsById(id)) {
+ throw new BookNotFoundException("Book not found with id " + id);
+ }
+ bookRepository.updateBookById(id, bookDto.getTitle(),
+ bookDto.getAuthor(), bookDto.getIsbn(),
+ bookDto.getPrice(), bookDto.getDescription(),
+ bookDto.getCoverImage());
+ }
}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 89c3eb7..8e85d49 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1,7 +1,8 @@
-spring.datasource.url=jdbc:mysql://localhost:3306/svitlana?serverTimezone=UTC
+spring.datasource.url=jdbc:mysql://localhost:3306/lombok?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=Wetuop34!
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
-spring.jpa.hibernate.ddl-auto=create-drop
-spring.jpa.show-sql=true
\ No newline at end of file
+spring.jpa.hibernate.ddl-auto=validate
+spring.jpa.show-sql=true
+spring.liquibase.enabled=false
\ No newline at end of file
diff --git a/src/main/resources/db.changelog/changes/01-test.yaml b/src/main/resources/db.changelog/changes/01-test.yaml
new file mode 100644
index 0000000..d1ffc18
--- /dev/null
+++ b/src/main/resources/db.changelog/changes/01-test.yaml
@@ -0,0 +1,5 @@
+databaseChangeLog:
+ - changeSet:
+ id: 01-test.yaml
+ author: artur
+ changes:
diff --git a/src/main/resources/db.changelog/db.changelog-master.yaml b/src/main/resources/db.changelog/db.changelog-master.yaml
new file mode 100644
index 0000000..7ae7003
--- /dev/null
+++ b/src/main/resources/db.changelog/db.changelog-master.yaml
@@ -0,0 +1,3 @@
+databaseChangeLog:
+ - include:
+ file:
\ No newline at end of file
diff --git a/src/main/resources/liquibase.properties b/src/main/resources/liquibase.properties
new file mode 100644
index 0000000..5b0fb64
--- /dev/null
+++ b/src/main/resources/liquibase.properties
@@ -0,0 +1,7 @@
+url=jdbc:mysql://localhost:3306/lombok?serverTimezone=UTC
+username=root
+password=Wetuop34!
+changeLogFile=src/main/resources/db/changelog/db.changelog-master.yaml
+driver=com.mysql.cj.jdbc.Driver
+
+
diff --git a/src/test/java/org/example/SpringBootBookShopApplicationTests.java b/src/test/java/org/example/SpringBootBookShopApplicationTests.java
index e491a4d..cb2c0bf 100644
--- a/src/test/java/org/example/SpringBootBookShopApplicationTests.java
+++ b/src/test/java/org/example/SpringBootBookShopApplicationTests.java
@@ -1,13 +1,8 @@
package org.example;
-import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class SpringBootBookShopApplicationTests {
- @Test
- void contextLoads() {
- }
-
}