백엔드/스프링, 스프링부트, JPA, Spring Webflux

[Springboot][개발] FLAG - Category CRUD 1차 완성 (Merged)

Kangjieun11 2022. 12. 21. 02:32
728x90


테스트 코드는 아직 어려워서, 요청메세지를 통한 테스트만 확실하게 진행한 상태로 컴밋을 진행하기 위해 코드를 기록한다.

테스트 완료 글


https://jie0025.tistory.com/338

 

[현재 상황 분석] 어디가 문제일지 & 앞으로 봐야할 부분들 찾기

JIE0025 [현재 상황 분석] 어디가 문제일지 & 앞으로 봐야할 부분들 찾기 본문 프로젝트 - FLAG/테스팅 [현재 상황 분석] 어디가 문제일지 & 앞으로 봐야할 부분들 찾기 Kangjieun11 2022. 12. 19. 23:36

jie0025.tistory.com


https://jie0025.tistory.com/343

 

[Postman][5] PUT- 카테고리 수정(컨트롤러 요청메세지 테스트)(PATCH 안쓸거다 ㅜㅜ)

JIE0025 [Postman][5] PUT- 카테고리 수정(컨트롤러 요청메세지 테스트)(PATCH 안쓸거다 ㅜㅜ) 본문 프로젝트 - FLAG/테스팅 [Postman][5] PUT- 카테고리 수정(컨트롤러 요청메세지 테스트)(PATCH 안쓸거다 ㅜㅜ) K

jie0025.tistory.com

 


 

✅ 디렉터리 구조

 

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가지방법중으로 바꾸기! 영속성 컨텍스트 사용하지 말기!)






드디어 프로젝트 참여하고 스터디만 하다가 첫 머징, 커밋이 완료됐다!! 개선해야 할 부분이 아직 있지만 이러면서 성장하는거니까 열심히 해야지😆