diff --git a/pom.xml b/pom.xml
index cf704585..6f47656d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -30,6 +30,33 @@
org.springframework.boot
spring-boot-autoconfigure
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ com.h2database
+ h2
+ runtime
+
+
+ org.projectlombok
+ lombok
+ 1.18.28
+ provided
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-devtools
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
diff --git a/src/main/java/mate/academy/springboot/datajpa/controller/CategoryController.java b/src/main/java/mate/academy/springboot/datajpa/controller/CategoryController.java
new file mode 100644
index 00000000..4512a708
--- /dev/null
+++ b/src/main/java/mate/academy/springboot/datajpa/controller/CategoryController.java
@@ -0,0 +1,52 @@
+package mate.academy.springboot.datajpa.controller;
+
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import mate.academy.springboot.datajpa.dto.request.CategoryRequestDto;
+import mate.academy.springboot.datajpa.dto.response.CategoryResponseDto;
+import mate.academy.springboot.datajpa.mapper.RequestDtoMapper;
+import mate.academy.springboot.datajpa.mapper.ResponseDtoMapper;
+import mate.academy.springboot.datajpa.model.Category;
+import mate.academy.springboot.datajpa.service.CategoryService;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/categories")
+@RequiredArgsConstructor
+public class CategoryController {
+ private final CategoryService categoryService;
+ private final ResponseDtoMapper responseDtoMapper;
+ private final RequestDtoMapper requestDtoMapper;
+
+ @PostMapping
+ public CategoryResponseDto create(@RequestBody @Valid CategoryRequestDto requestDto) {
+ Category category = categoryService.create(requestDtoMapper
+ .mapToModel(requestDto));
+ return responseDtoMapper.mapToDto(category);
+ }
+
+ @GetMapping("/{id}")
+ public CategoryResponseDto get(@PathVariable Long id) {
+ Category category = categoryService.getById(id);
+ return responseDtoMapper.mapToDto(category);
+ }
+
+ @DeleteMapping("/{id}")
+ public void delete(@PathVariable Long id) {
+ categoryService.delete(id);
+ }
+
+ @PutMapping("/{id}")
+ public CategoryResponseDto update(@RequestBody @Valid CategoryRequestDto requestDto,
+ @PathVariable Long id) {
+ Category category = categoryService.update(requestDtoMapper.mapToModel(requestDto), id);
+ return responseDtoMapper.mapToDto(category);
+ }
+}
diff --git a/src/main/java/mate/academy/springboot/datajpa/controller/ProductController.java b/src/main/java/mate/academy/springboot/datajpa/controller/ProductController.java
new file mode 100644
index 00000000..6982d817
--- /dev/null
+++ b/src/main/java/mate/academy/springboot/datajpa/controller/ProductController.java
@@ -0,0 +1,73 @@
+package mate.academy.springboot.datajpa.controller;
+
+import jakarta.validation.Valid;
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.stream.Collectors;
+import lombok.RequiredArgsConstructor;
+import mate.academy.springboot.datajpa.dto.request.ProductRequestDto;
+import mate.academy.springboot.datajpa.dto.response.ProductResponseDto;
+import mate.academy.springboot.datajpa.mapper.RequestDtoMapper;
+import mate.academy.springboot.datajpa.mapper.ResponseDtoMapper;
+import mate.academy.springboot.datajpa.model.Product;
+import mate.academy.springboot.datajpa.service.ProductService;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/products")
+@RequiredArgsConstructor
+public class ProductController {
+ private final ProductService productService;
+ private final ResponseDtoMapper responseDtoMapper;
+ private final RequestDtoMapper requestDtoMapper;
+
+ @PostMapping
+ public ProductResponseDto create(@RequestBody @Valid ProductRequestDto productRequestDto) {
+ Product product = productService.create(requestDtoMapper.mapToModel(productRequestDto));
+ return responseDtoMapper.mapToDto(product);
+ }
+
+ @GetMapping("/{id}")
+ public ProductResponseDto getById(@PathVariable Long id) {
+ Product product = productService.getById(id);
+ return responseDtoMapper.mapToDto(product);
+ }
+
+ @DeleteMapping("/{id}")
+ public void delete(@PathVariable Long id) {
+ productService.delete(id);
+ }
+
+ @PutMapping("/{id}")
+ public ProductResponseDto update(@PathVariable Long id,
+ @RequestBody @Valid ProductRequestDto productRequestDto) {
+ Product product = productService.update(requestDtoMapper
+ .mapToModel(productRequestDto), id);
+ return responseDtoMapper.mapToDto(product);
+ }
+
+ @GetMapping("/price")
+ public List getAllByPriceBetween(@RequestParam BigDecimal from,
+ @RequestParam BigDecimal to) {
+ List products = productService.getAllByPriceBetween(from, to);
+ return products.stream()
+ .map(responseDtoMapper::mapToDto)
+ .collect(Collectors.toList());
+ }
+
+ @GetMapping("/category")
+ public List getAllByCategories(@RequestParam List categoryNames) {
+ List products = productService.getAllByCategoryNames(categoryNames);
+ return products.stream()
+ .map(responseDtoMapper::mapToDto)
+ .collect(Collectors.toList());
+ }
+}
diff --git a/src/main/java/mate/academy/springboot/datajpa/dao/CategoryRepository.java b/src/main/java/mate/academy/springboot/datajpa/dao/CategoryRepository.java
new file mode 100644
index 00000000..2f05c152
--- /dev/null
+++ b/src/main/java/mate/academy/springboot/datajpa/dao/CategoryRepository.java
@@ -0,0 +1,9 @@
+package mate.academy.springboot.datajpa.dao;
+
+import mate.academy.springboot.datajpa.model.Category;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface CategoryRepository extends JpaRepository {
+}
diff --git a/src/main/java/mate/academy/springboot/datajpa/dao/ProductRepository.java b/src/main/java/mate/academy/springboot/datajpa/dao/ProductRepository.java
new file mode 100644
index 00000000..99b0d757
--- /dev/null
+++ b/src/main/java/mate/academy/springboot/datajpa/dao/ProductRepository.java
@@ -0,0 +1,14 @@
+package mate.academy.springboot.datajpa.dao;
+
+import java.math.BigDecimal;
+import java.util.List;
+import mate.academy.springboot.datajpa.model.Product;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface ProductRepository extends JpaRepository {
+ List findAllByPriceBetween(BigDecimal from, BigDecimal to);
+
+ List findByCategoryNameIn(List categoryNames);
+}
diff --git a/src/main/java/mate/academy/springboot/datajpa/dto/request/CategoryRequestDto.java b/src/main/java/mate/academy/springboot/datajpa/dto/request/CategoryRequestDto.java
new file mode 100644
index 00000000..67ab1767
--- /dev/null
+++ b/src/main/java/mate/academy/springboot/datajpa/dto/request/CategoryRequestDto.java
@@ -0,0 +1,12 @@
+package mate.academy.springboot.datajpa.dto.request;
+
+import jakarta.validation.constraints.NotNull;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class CategoryRequestDto {
+ @NotNull
+ private String name;
+}
diff --git a/src/main/java/mate/academy/springboot/datajpa/dto/request/ProductRequestDto.java b/src/main/java/mate/academy/springboot/datajpa/dto/request/ProductRequestDto.java
new file mode 100644
index 00000000..7ead3b3e
--- /dev/null
+++ b/src/main/java/mate/academy/springboot/datajpa/dto/request/ProductRequestDto.java
@@ -0,0 +1,19 @@
+package mate.academy.springboot.datajpa.dto.request;
+
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Positive;
+import java.math.BigDecimal;
+import lombok.Getter;
+import lombok.Setter;
+
+@Setter
+@Getter
+public class ProductRequestDto {
+ @NotNull
+ private String title;
+ @Positive
+ @NotNull
+ private BigDecimal price;
+ @NotNull
+ private Long categoryId;
+}
diff --git a/src/main/java/mate/academy/springboot/datajpa/dto/response/CategoryResponseDto.java b/src/main/java/mate/academy/springboot/datajpa/dto/response/CategoryResponseDto.java
new file mode 100644
index 00000000..56fd0b65
--- /dev/null
+++ b/src/main/java/mate/academy/springboot/datajpa/dto/response/CategoryResponseDto.java
@@ -0,0 +1,11 @@
+package mate.academy.springboot.datajpa.dto.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Setter
+@Getter
+public class CategoryResponseDto {
+ private Long id;
+ private String name;
+}
diff --git a/src/main/java/mate/academy/springboot/datajpa/dto/response/ProductResponseDto.java b/src/main/java/mate/academy/springboot/datajpa/dto/response/ProductResponseDto.java
new file mode 100644
index 00000000..1ed6b0d4
--- /dev/null
+++ b/src/main/java/mate/academy/springboot/datajpa/dto/response/ProductResponseDto.java
@@ -0,0 +1,14 @@
+package mate.academy.springboot.datajpa.dto.response;
+
+import java.math.BigDecimal;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class ProductResponseDto {
+ private Long id;
+ private String title;
+ private BigDecimal price;
+ private Long categoryId;
+}
diff --git a/src/main/java/mate/academy/springboot/datajpa/mapper/RequestDtoMapper.java b/src/main/java/mate/academy/springboot/datajpa/mapper/RequestDtoMapper.java
new file mode 100644
index 00000000..92248eea
--- /dev/null
+++ b/src/main/java/mate/academy/springboot/datajpa/mapper/RequestDtoMapper.java
@@ -0,0 +1,5 @@
+package mate.academy.springboot.datajpa.mapper;
+
+public interface RequestDtoMapper {
+ T mapToModel(D dto);
+}
diff --git a/src/main/java/mate/academy/springboot/datajpa/mapper/ResponseDtoMapper.java b/src/main/java/mate/academy/springboot/datajpa/mapper/ResponseDtoMapper.java
new file mode 100644
index 00000000..86792512
--- /dev/null
+++ b/src/main/java/mate/academy/springboot/datajpa/mapper/ResponseDtoMapper.java
@@ -0,0 +1,5 @@
+package mate.academy.springboot.datajpa.mapper;
+
+public interface ResponseDtoMapper {
+ D mapToDto(T model);
+}
diff --git a/src/main/java/mate/academy/springboot/datajpa/mapper/impl/CategoryRequestDtoMapper.java b/src/main/java/mate/academy/springboot/datajpa/mapper/impl/CategoryRequestDtoMapper.java
new file mode 100644
index 00000000..a62fdeb3
--- /dev/null
+++ b/src/main/java/mate/academy/springboot/datajpa/mapper/impl/CategoryRequestDtoMapper.java
@@ -0,0 +1,16 @@
+package mate.academy.springboot.datajpa.mapper.impl;
+
+import mate.academy.springboot.datajpa.dto.request.CategoryRequestDto;
+import mate.academy.springboot.datajpa.mapper.RequestDtoMapper;
+import mate.academy.springboot.datajpa.model.Category;
+import org.springframework.stereotype.Component;
+
+@Component
+public class CategoryRequestDtoMapper implements RequestDtoMapper {
+ @Override
+ public Category mapToModel(CategoryRequestDto dto) {
+ Category category = new Category();
+ category.setName(dto.getName());
+ return category;
+ }
+}
diff --git a/src/main/java/mate/academy/springboot/datajpa/mapper/impl/CategoryResponseDtoMapper.java b/src/main/java/mate/academy/springboot/datajpa/mapper/impl/CategoryResponseDtoMapper.java
new file mode 100644
index 00000000..2d545395
--- /dev/null
+++ b/src/main/java/mate/academy/springboot/datajpa/mapper/impl/CategoryResponseDtoMapper.java
@@ -0,0 +1,17 @@
+package mate.academy.springboot.datajpa.mapper.impl;
+
+import mate.academy.springboot.datajpa.dto.response.CategoryResponseDto;
+import mate.academy.springboot.datajpa.mapper.ResponseDtoMapper;
+import mate.academy.springboot.datajpa.model.Category;
+import org.springframework.stereotype.Component;
+
+@Component
+public class CategoryResponseDtoMapper implements ResponseDtoMapper {
+ @Override
+ public CategoryResponseDto mapToDto(Category category) {
+ CategoryResponseDto categoryResponseDto = new CategoryResponseDto();
+ categoryResponseDto.setId(category.getId());
+ categoryResponseDto.setName(category.getName());
+ return categoryResponseDto;
+ }
+}
diff --git a/src/main/java/mate/academy/springboot/datajpa/mapper/impl/ProductRequestDtoMapper.java b/src/main/java/mate/academy/springboot/datajpa/mapper/impl/ProductRequestDtoMapper.java
new file mode 100644
index 00000000..3dd5dd1c
--- /dev/null
+++ b/src/main/java/mate/academy/springboot/datajpa/mapper/impl/ProductRequestDtoMapper.java
@@ -0,0 +1,19 @@
+package mate.academy.springboot.datajpa.mapper.impl;
+
+import mate.academy.springboot.datajpa.dto.request.ProductRequestDto;
+import mate.academy.springboot.datajpa.mapper.RequestDtoMapper;
+import mate.academy.springboot.datajpa.model.Category;
+import mate.academy.springboot.datajpa.model.Product;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ProductRequestDtoMapper implements RequestDtoMapper {
+ @Override
+ public Product mapToModel(ProductRequestDto dto) {
+ Product product = new Product();
+ product.setTitle(dto.getTitle());
+ product.setPrice(dto.getPrice());
+ product.setCategory(new Category(dto.getCategoryId()));
+ return product;
+ }
+}
diff --git a/src/main/java/mate/academy/springboot/datajpa/mapper/impl/ProductResponseDtoMapper.java b/src/main/java/mate/academy/springboot/datajpa/mapper/impl/ProductResponseDtoMapper.java
new file mode 100644
index 00000000..93a195ec
--- /dev/null
+++ b/src/main/java/mate/academy/springboot/datajpa/mapper/impl/ProductResponseDtoMapper.java
@@ -0,0 +1,19 @@
+package mate.academy.springboot.datajpa.mapper.impl;
+
+import mate.academy.springboot.datajpa.dto.response.ProductResponseDto;
+import mate.academy.springboot.datajpa.mapper.ResponseDtoMapper;
+import mate.academy.springboot.datajpa.model.Product;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ProductResponseDtoMapper implements ResponseDtoMapper {
+ @Override
+ public ProductResponseDto mapToDto(Product product) {
+ ProductResponseDto productResponseDto = new ProductResponseDto();
+ productResponseDto.setId(product.getId());
+ productResponseDto.setTitle(product.getTitle());
+ productResponseDto.setPrice(product.getPrice());
+ productResponseDto.setCategoryId(product.getCategory().getId());
+ return productResponseDto;
+ }
+}
diff --git a/src/main/java/mate/academy/springboot/datajpa/model/Category.java b/src/main/java/mate/academy/springboot/datajpa/model/Category.java
new file mode 100644
index 00000000..588e76c1
--- /dev/null
+++ b/src/main/java/mate/academy/springboot/datajpa/model/Category.java
@@ -0,0 +1,28 @@
+package mate.academy.springboot.datajpa.model;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import lombok.ToString;
+
+@Entity
+@Table(name = "categories")
+@Setter
+@Getter
+@NoArgsConstructor
+@ToString
+public class Category {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+ private String name;
+
+ public Category(Long id) {
+ this.id = id;
+ }
+}
diff --git a/src/main/java/mate/academy/springboot/datajpa/model/Product.java b/src/main/java/mate/academy/springboot/datajpa/model/Product.java
new file mode 100644
index 00000000..7d8e0365
--- /dev/null
+++ b/src/main/java/mate/academy/springboot/datajpa/model/Product.java
@@ -0,0 +1,35 @@
+package mate.academy.springboot.datajpa.model;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.ManyToOne;
+import jakarta.persistence.Table;
+import java.math.BigDecimal;
+import lombok.Getter;
+import lombok.Setter;
+
+@Entity
+@Table(name = "products")
+@Setter
+@Getter
+public class Product {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+ private String title;
+ private BigDecimal price;
+ @ManyToOne(fetch = FetchType.LAZY)
+ private Category category;
+
+ @Override
+ public String toString() {
+ return "Product{"
+ + "id=" + id
+ + ", title='" + title + '\''
+ + ", price=" + price
+ + '}';
+ }
+}
diff --git a/src/main/java/mate/academy/springboot/datajpa/service/CategoryService.java b/src/main/java/mate/academy/springboot/datajpa/service/CategoryService.java
new file mode 100644
index 00000000..c7e11c9f
--- /dev/null
+++ b/src/main/java/mate/academy/springboot/datajpa/service/CategoryService.java
@@ -0,0 +1,13 @@
+package mate.academy.springboot.datajpa.service;
+
+import mate.academy.springboot.datajpa.model.Category;
+
+public interface CategoryService {
+ Category create(Category category);
+
+ Category getById(Long id);
+
+ void delete(Long id);
+
+ Category update(Category category, Long id);
+}
diff --git a/src/main/java/mate/academy/springboot/datajpa/service/ProductService.java b/src/main/java/mate/academy/springboot/datajpa/service/ProductService.java
new file mode 100644
index 00000000..039586ed
--- /dev/null
+++ b/src/main/java/mate/academy/springboot/datajpa/service/ProductService.java
@@ -0,0 +1,19 @@
+package mate.academy.springboot.datajpa.service;
+
+import java.math.BigDecimal;
+import java.util.List;
+import mate.academy.springboot.datajpa.model.Product;
+
+public interface ProductService {
+ Product create(Product product);
+
+ Product getById(Long id);
+
+ void delete(Long id);
+
+ Product update(Product product, Long id);
+
+ List getAllByPriceBetween(BigDecimal from, BigDecimal to);
+
+ List getAllByCategoryNames(List categoryNames);
+}
diff --git a/src/main/java/mate/academy/springboot/datajpa/service/impl/CategoryServiceImpl.java b/src/main/java/mate/academy/springboot/datajpa/service/impl/CategoryServiceImpl.java
new file mode 100644
index 00000000..3fe09ef5
--- /dev/null
+++ b/src/main/java/mate/academy/springboot/datajpa/service/impl/CategoryServiceImpl.java
@@ -0,0 +1,38 @@
+package mate.academy.springboot.datajpa.service.impl;
+
+import java.util.NoSuchElementException;
+import lombok.RequiredArgsConstructor;
+import mate.academy.springboot.datajpa.dao.CategoryRepository;
+import mate.academy.springboot.datajpa.model.Category;
+import mate.academy.springboot.datajpa.service.CategoryService;
+import org.springframework.stereotype.Service;
+
+@Service
+@RequiredArgsConstructor
+public class CategoryServiceImpl implements CategoryService {
+ private final CategoryRepository categoryRepository;
+
+ @Override
+ public Category create(Category category) {
+ return categoryRepository.save(category);
+ }
+
+ @Override
+ public Category getById(Long id) {
+ return categoryRepository.findById(id)
+ .orElseThrow(() -> new NoSuchElementException("Can't find the category by "
+ + "id: " + id));
+ }
+
+ @Override
+ public void delete(Long id) {
+ categoryRepository.deleteById(id);
+ }
+
+ @Override
+ public Category update(Category category, Long id) {
+ Category oldCategory = getById(id);
+ oldCategory.setName(category.getName());
+ return categoryRepository.save(oldCategory);
+ }
+}
diff --git a/src/main/java/mate/academy/springboot/datajpa/service/impl/ProductServiceImpl.java b/src/main/java/mate/academy/springboot/datajpa/service/impl/ProductServiceImpl.java
new file mode 100644
index 00000000..a32ed3e4
--- /dev/null
+++ b/src/main/java/mate/academy/springboot/datajpa/service/impl/ProductServiceImpl.java
@@ -0,0 +1,51 @@
+package mate.academy.springboot.datajpa.service.impl;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.NoSuchElementException;
+import lombok.RequiredArgsConstructor;
+import mate.academy.springboot.datajpa.dao.ProductRepository;
+import mate.academy.springboot.datajpa.model.Product;
+import mate.academy.springboot.datajpa.service.ProductService;
+import org.springframework.stereotype.Service;
+
+@Service
+@RequiredArgsConstructor
+public class ProductServiceImpl implements ProductService {
+ private final ProductRepository productRepository;
+
+ @Override
+ public Product create(Product product) {
+ return productRepository.save(product);
+ }
+
+ @Override
+ public Product getById(Long id) {
+ return productRepository.findById(id)
+ .orElseThrow(() -> new NoSuchElementException("Product not found with id: " + id));
+ }
+
+ @Override
+ public void delete(Long id) {
+ productRepository.deleteById(id);
+ }
+
+ @Override
+ public Product update(Product product, Long id) {
+ Product oldProduct = getById(id);
+ oldProduct.setCategory(product.getCategory());
+ oldProduct.setTitle(product.getTitle());
+ oldProduct.setPrice(product.getPrice());
+ return productRepository.save(oldProduct);
+ }
+
+ @Override
+ public List getAllByPriceBetween(BigDecimal from, BigDecimal to) {
+ return productRepository.findAllByPriceBetween(from, to);
+ }
+
+ @Override
+ public List getAllByCategoryNames(List categoryNames) {
+ return productRepository.findByCategoryNameIn(categoryNames);
+ }
+}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 8b137891..78aa24bd 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1 +1,8 @@
+spring.datasource.url=jdbc:h2:mem:springboot_db
+spring.datasource.driverClassName=org.h2.Driver
+spring.datasource.username=user
+spring.datasource.password=1234
+spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
+spring.h2.console.enabled=true
+spring.jpa.show-sql=true