[SpringBoot] Repository/Service/Controller 계층 개발

2023. 5. 9. 05:50
반응형

Repository

Repository는 데이터베이스와의 상호 작용을 담당하는 모듈 계층이라고 하였다.

 

 

 

Repository 구조 작성

@Repository
@RequiredArgsConstructor
public class ItemRepository {
    private final EntityManager em;

}
  • 어노테이션으로 Spring Boot에 Repository임을 명시해준다.
  • 엔티티와 상호작용 하기 위해서 EntityManager를 의존성 주입받는다.

 

 

Select

@Repository
@RequiredArgsConstructor
public class ItemRepository {
    private final EntityManager em;


    public Item findOne(Long id){
        return em.find(Item.class, id);
    }


    public List<Item> findAll(){
        return em.createQuery("select i from Item i", Item.class).getResultList();
    }
}
  • EntityManager를 통해 엔티티를 Select하는 방법이다.
  • em.find() : 해당하는 엔터티 클래스를 기본키를 통해 데이터 조회한다.
  • em.createQuery() : JPQL 문법을 통하여 데이터를 조회한다.

 

 

Insert / Update

@Repository
@RequiredArgsConstructor
public class ItemRepository {
    private final EntityManager em;


    public void save(Item item){
        //JPA는 Item에 넣기 전까지는 ID가 없음 -> 신규값
        if(item.getId() == null){
            em.persist((item));
        }
        //DB에 등록된 값을 가져온 것
        else{
            em.merge(item); 
            //Update와 비슷한 Merge. 준영속 컨텍스트를 영속성 컨텍스트 객체로 변환하여 반환해준다.
            // (파라미터인 item이 변환되는 것은 아니고 새로운 영속성 객체가 반환됨)

        }
    }
}
  • 기본키가 없다면 Insert, 없다면 Update하는 로직
  • em.persist() : 해당 Entity Object를 데이터베이스에 등록한다.
  • em.merge() : 해당 Entity Object를 수정한다. 이것은 사실 준영속 컨텍스트 객체를 영속성 컨텍스트가 관리하는 형태로 변환하는 것. 해당 내용에 대해서는 다음 포스팅에서 자세하게 다루어보겠다.

 

 


Service

Service는 Controller와 Repository 사이에서 비즈니스 로직을 처리하는 계층이다.

 

 

Service 구조 작성

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class ItemService {
    private final ItemRepository itemRepository;

    

}
  • 어노테이션으로 Spring Boot에 Service임을 명시해준다.
  • 작성한 Repository를 이용하기 위해서 의존성 주입받는다.
  • @Transactional
    • 비즈니스 로직을 하나의 작업 단위로 묶어서 실행하는 트랜잭션을 통해 처리한다. 
    • 메서드 레벨에서 트랜잭션을 시작하고, 수행 중 예외가 발생하면 변경 사항을 롤백, 메서드 수행이 성공하면 커밋한다.
    • readOnly는 디폴트값이 False이지만, True로 두게되면 조회 시 성능이 향상된다. 그러나 Write가 되지 않는다.

 

 

 

조회

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class ItemService {
    private final ItemRepository itemRepository;


    public List<Item> findItems(){
        return itemRepository.findAll();
    }

    public Item findOne(Long itemId){
        return itemRepository.findOne(itemId);
    }



}
  • Repository를 통하여 데이터를 조회하는 메서드를 생성하였다.

 

 

생성 및 수정

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class ItemService {
    private final ItemRepository itemRepository;

    @Transactional
    public void saveItem(Item item){
        itemRepository.save(item);
    }

    @Transactional
    public Item updateItem(Item item){
        itemRepository.save(item);
    }


}
  • Repository를 통하여 데이터를 조회 및 수정하는 메서드를 생성하였다.
  • Write를 해야하므로 @Transactional(read = False)를 사용해야 한다. 따라서 어노테이션을 메서드에 Overwrite해주었다.
  • 수정 메서드의 경우 사실 좋은 방법이 아니다. Repository의 merge를 사용하게 되면 단점이 있는데 간단하게 말하고 가자면, 준영속 컨텍스트를 이는 다음 포스팅에서 다루어보도록 하겠다.

 

 


Controller

Controller는 클라이언트의 요청과 응답을 처리하는 계층이다. 요청에 대한 비즈니스 로직을 처리하고 필요한 데이터를 반환한다.

 

 

@Controller
@RequiredArgsConstructor
public class ItemController {
    private final ItemService itemService;

    @GetMapping("/items/new")
    public String createForm(Model model){
        model.addAttribute("form", new BookForm());
        return "items/createItemForm";
    }

    @PostMapping("/items/new")
    public String create(BookForm form){

        Book book = new Book();
        //Setter 대신 Create하는 메서드를 엔티티에 만드는 것이 더 좋긴 함!!
        book.setName(form.getName());
        book.setPrice(form.getPrice());
        book.setStockQuantity(form.getStockQuantity());
        book.setAuthor((form.getAuthor()));
        book.setIsbn(form.getIsbn());

        itemService.saveItem(book);
        return "redirect:/";

    }

    @GetMapping("/items")
    public String list(Model model){
        List<Item> items = itemService.findItems();
        model.addAttribute("items", items);
        return "items/itemList";
    }


    @GetMapping("/items/{itemId}/edit")
    public String updateItemForm(@PathVariable("itemId") Long itemId, Model model){
        Book item = (Book) itemService.findOne(itemId);
        BookForm form = new BookForm();
        form.setId(item.getId());
        form.setName(item.getName());
        form.setPrice(item.getPrice());
        form.setStockQuantity(item.getStockQuantity());
        form.setIsbn(item.getIsbn());
        form.setAuthor(item.getAuthor());

        model.addAttribute("form", form);
        return "items/updateItemForm";
    }

    @PostMapping("/items/{itemId}/edit")
    public String updateItem(@PathVariable Long itemId, BookForm form) {       
        Book book = new Book();
        book.setId(form.getId());
        book.setName(form.getName());
        book.setPrice(form.getPrice());
        book.setStockQuantity(form.getStockQuantity());
        book.setAuthor((form.getAuthor()));
        book.setIsbn(form.getIsbn());

        itemService.saveItem(book);

        return "redirect:/items";
    }
}
  • 어노테이션으로 Spring Boot에 Controller임을 명시해주고, Service를 의존성 주입받는다.
  • @GetMapping과 @PostMapping을 통해 URL에 대한 Mapping을 수행한다.
  • @PathVariable은 URL의 Path를 Arg로 받을 때 사용하며, URL에서는 {} 안의 값이 Path Variable로 사용된다.
  • 사실 Setter를 이용하는 것은 좋은 방법이 아닐 수 있다. 엔티티 클래스 안에 Set해주는 메서드를 생성하여 사용하는 쪽을 권장한다고 한다.

 

 

 

다음 포스팅에서는 영속성 컨텍스트와 준영속 컨텍스트, 그리고 앞에서 언급하였던 merge를 바꾸어 보겠다.


https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-JPA-%ED%99%9C%EC%9A%A9-1

 

실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발 - 인프런 | 강의

실무에 가까운 예제로, 스프링 부트와 JPA를 활용해서 웹 애플리케이션을 설계하고 개발합니다. 이 과정을 통해 스프링 부트와 JPA를 실무에서 어떻게 활용해야 하는지 이해할 수 있습니다., - 강

www.inflearn.com

 

해당 포스팅은 본 강의 수강을 따라가면서 작성합니다.

반응형

BELATED ARTICLES

more