백엔드/스프링, 스프링부트, JPA, Spring Webflux
[Springboot][개발] FLAG - Category CRUD 1차 완성 (Merged)
Kangjieun11
2022. 12. 21. 02:32
728x90
테스트 코드는 아직 어려워서, 요청메세지를 통한 테스트만 확실하게 진행한 상태로 컴밋을 진행하기 위해 코드를 기록한다.
✅ 테스트 완료 글
https://jie0025.tistory.com/338
https://jie0025.tistory.com/343
✅ 디렉터리 구조
⏺ controller
package com.FlagHome.backend.v1.category.controller;
import com.FlagHome.backend.v1.category.dto.CategoryDto;
import com.FlagHome.backend.v1.category.entity.Category;
import com.FlagHome.backend.v1.category.service.CategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/v1/categories")
public class CategoryController {
private final CategoryService categoryService;
@Autowired
public CategoryController(CategoryService categoryService) {
this.categoryService = categoryService;
}
@PostMapping
public ResponseEntity createCategory(@RequestBody CategoryDto categoryDto) {
categoryService.createCategory(categoryDto);
return new ResponseEntity(HttpStatus.CREATED); //BODY 던져주기!
}
@PutMapping("/{categoryId}")
public ResponseEntity updateCategory(@PathVariable long categoryId,
@RequestBody CategoryDto categoryDto) {
categoryDto.setId(categoryId);
categoryService.updateCategory(categoryDto);
return new ResponseEntity(HttpStatus.OK);
}
@GetMapping
public ResponseEntity<?> getCategories() {
return ResponseEntity.ok(categoryService.getCategories());
}
@DeleteMapping("/{categoryId}")
public ResponseEntity deleteCatgory(@PathVariable long categoryId) {
categoryService.deleteCategory(categoryId);
return new ResponseEntity(HttpStatus.NO_CONTENT);
}
}
⏺ dto
package com.FlagHome.backend.v1.category.dto;
import com.FlagHome.backend.v1.category.entity.Category;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Setter
@NoArgsConstructor //테스트용
@AllArgsConstructor
@Getter
public class CategoryDto {
private Long id;
private Long parentId;
private String name;
private Long categoryDepth;
public CategoryDto(Category category){
this.id = category.getId();
this.parentId = category.getParent().getId();
this.name = category.getName();
this.categoryDepth = category.getCategoryDepth();
}
}
⏺ entity
💻 Category
package com.FlagHome.backend.v1.category.entity;
import lombok.*;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
@Setter
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Category {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String name;
@Column
private Long categoryDepth;
@ManyToOne(fetch = FetchType.LAZY) //지연로딩, Category 조회할 때 부모도 함께 조회할 필요는 없고, 해당 카테고리의 자식들은 필요함.
@JoinColumn(name="parent")
private Category parent;
//cateorgy : parent = n : 1
//외래키가 존재하는 곳이 곧 '연관관계의 주인'이다.
//name 속성은 말 그래도 Team 엔티티에 존재하는 member 라는 필드를 어떤 이름으로 Team 테이블에 컬럼명으로 설정할 것인지를 나타내주는 것이다.
//지연로딩할 필요는 없을듯
@OneToMany(mappedBy = "parent")
private List<Category> children= new ArrayList<>();
//하위 메뉴를 가져오기 위한 1:n
}
💻 CategoryResult
package com.FlagHome.backend.v1.category.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.util.List;
import java.util.stream.Collectors;
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class CategoryResult{
private Long id;
private String name;
private Long depth;
private List<CategoryResult> children;
public static CategoryResult of(Category category) {
return new CategoryResult(
category.getId(),
category.getName(),
category.getCategoryDepth(),
category.getChildren().stream().map(CategoryResult::of).collect(Collectors.toList())
);
}
}
⏺ repository
💻 CategoryCustomRepositoy
package com.FlagHome.backend.v1.category.repository;
import com.FlagHome.backend.v1.category.entity.Category;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import javax.persistence.EntityManager;
import java.util.List;
@Repository
@RequiredArgsConstructor
public class CategoryCustomRepository {
private final EntityManager em;
public List<Category> findAll(){
return em.createQuery(
"select c " +
"from Category c " +
"where c.parent is NULL"
,Category.class)
.getResultList();
}
}
💻 CategoryRepositoy
package com.FlagHome.backend.v1.category.repository;
import com.FlagHome.backend.v1.category.entity.Category;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface CategoryRepository extends JpaRepository<Category,Long> {
}
⏺ service
package com.FlagHome.backend.v1.category.service;
import com.FlagHome.backend.global.exception.CustomException;
import com.FlagHome.backend.global.exception.ErrorCode;
import com.FlagHome.backend.v1.category.dto.CategoryDto;
import com.FlagHome.backend.v1.category.entity.Category;
import com.FlagHome.backend.v1.category.entity.CategoryResult;
import com.FlagHome.backend.v1.category.repository.CategoryCustomRepository;
import com.FlagHome.backend.v1.category.repository.CategoryRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
import java.util.List;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class CategoryService {
private CategoryRepository categoryRepository;
private CategoryCustomRepository categoryCustomRepository;
@Autowired
public CategoryService(CategoryRepository categoryRepository, CategoryCustomRepository categoryCustomRepository) {
this.categoryRepository = categoryRepository;
this.categoryCustomRepository = categoryCustomRepository;
}
@Transactional
public void createCategory (CategoryDto categoryDto) {
//부모 정보가 있는지 확인하고, 있으면 저장
Category parentCategory = null;
//depth가 0이 아닌 친구들의 부모 존재 체크
if(categoryDto.getCategoryDepth()!=0){
parentCategory = categoryRepository.findById(categoryDto.getParentId()).orElse(null);
if(parentCategory == null)
throw new CustomException(ErrorCode.CATEGORY_NOT_EXISTS);
}
Category category = Category.builder()
.name(categoryDto.getName())
.parent(parentCategory)
.categoryDepth(categoryDto.getCategoryDepth())
.build();
//데이터 저장
categoryRepository.save(category);
//부모의 자식에 추가
if(parentCategory != null)
parentCategory.getChildren().add(category);
}
@Transactional
public void updateCategory (CategoryDto categoryDto) {
Category category = categoryRepository.findById(categoryDto.getId()).orElse(null);
if(category == null) {
throw new CustomException(ErrorCode.CATEGORY_NOT_EXISTS);
}
Category parentCategory = null;
if (categoryDto.getCategoryDepth() > 0){
parentCategory = categoryRepository.findById(categoryDto.getParentId()).orElse(null);
}
category.setName(categoryDto.getName());
category.setParent(parentCategory);
category.setCategoryDepth(categoryDto.getCategoryDepth());
/*
if(category.getName() != categoryDto.getName())
category.setName(categoryDto.getName());
if(category.getParent() != parentCategory)
category.setParent(parentCategory);
if(category.getCategoryDepth() != categoryDto.getCategoryDepth())
category.setCategoryDepth(categoryDto.getCategoryDepth());
*/
//categoryRepository.save(category);
}
@Transactional
public List<CategoryResult> getCategories () {
//List<Category> categories = categoryRepository.findAll();
List<CategoryResult> categories = categoryCustomRepository
.findAll().stream().map(CategoryResult::of)
.collect(Collectors.toList());
return categories;
}
@Transactional
public void deleteCategory (long categoryId) {
categoryRepository.deleteById(categoryId);
}
}
✅ 개선해야할점
- 아직 POST에 적용은 안했다.
- 엔티티와 레포지토리가 두개씩 있는점에서 개선이 필요하다 ㅠㅠ 일단 동작하니까.. (3가지방법중으로 바꾸기! 영속성 컨텍스트 사용하지 말기!)
드디어 프로젝트 참여하고 스터디만 하다가 첫 머징, 커밋이 완료됐다!! 개선해야 할 부분이 아직 있지만 이러면서 성장하는거니까 열심히 해야지😆