inblog logo
|
jay0628
    SpringBoot

    [Spring Boot] 16. 스프링부트 상점 v1 (7) Join된 데이터를 담는 방법 - Log list

    김주희's avatar
    김주희
    Mar 20, 2025
    [Spring Boot] 16. 스프링부트 상점 v1 (7) Join된 데이터를 담는 방법 - Log list
    Contents
    1. Join 먼저2. LogRepository로 가자!3. LogResponse.ListPage (DTO)4. 다시 LogRepository를 완성하러 가자5. LogController를 만들자6. LogService 만들기7. log/list.mustache 완성하기

    1. Join 먼저

    1. log/list.mustache부터 보자!

    1. “주문번호/상품명/구매개수/총가격/구매자이름”이 필요하다는 것을 알 수 있다.
    1. 여기서 바나나는 log_tb를 통해 알 수 있는 것이 아니다
    1. log_tb에서는 store_id를 알 수 있고 store_id를 통해 상품명을 가져오게 된다.
    notion image
     

    2. Join 쿼리를 짜자!

    1. from절에서 두 개의 테이블을 Inner Join한다.
    1. 테이블을 묶어줄 속성을 찾아서 ON절에 적는다.
    1. select절에는 list.mustache에서 찾은 필요한 컬럼을 적는다.
    1. 목록은 id를 내림차순으로 적는게 좋다.
    1. 조인하기 위해 테이블의 별칭을 사용했기 때문에 모든 컬럼을 쓸 때에는 테이블 별칭.컬럼명으로 써야한다.
    notion image
     

    2. LogRepository로 가자!

    1. 수동 Object Mapping

    1. EntityManager는 JPA가 관리해주는 @Entity가 붙어있는 Entity만 매핑 가능하다.
      1. Entity란 JPA가 관리하는 데이터베이스의 테이블과 매핑되는 자바 객체
    1. Join한 데이터는 Store.class나 Log.class와 같은 Entity가 아니기 때문에 자동 매핑이 불가능하다.
    1. createNativeQuery 메서드는 Query 객체를 만들어낸다.
    1. 쿼리의 실행 결과가 여러 개의 행으로 리스트 형태일 경우 getResultList 메서드를 사용한다.
    1. Object[]는 한 개의 행을 의미한다.
      1. Object[] = [Ob, Ob, Ob, Ob, Ob] => [id, name, qyt, total_price, buyer]를 의미한다.
      2. List<Object[]> = [Ob, Ob, Ob, Ob] [Ob, Ob, Ob, Ob] … -> 하나하나가 row
      3. 번지로 parsing해서 꺼내야 된다.
    1. 따라서 Object[] 타입의 리스트를 리턴 타입으로 지정한다.
    @Repository // IoC public class LogRepository { private EntityManager em; // DI public LogRepository(EntityManager em) { this.em = em; } public void findAllJoinStore(){ String q = "SELECT lt.id, st.name, lt.qty, lt.total_price, lt.buyer FROM log_tb lt INNER JOIN store_tb st ON lt.store_id = st.id"; Query query = em.createNativeQuery(q); List<Object[]> obsList = (List<Object[]>) query.getResultList(); // Object[] -> ROW ([id, name, qyt, total_price, buyer]) for(Object[] obs : obsList){ System.out.print(obs[0]); System.out.print(obs[1]); System.out.print(obs[2]); System.out.print(obs[3]); System.out.print(obs[4]); System.out.println("================="); } } }
     

    3. LogResponse.ListPage (DTO)

    1. DTO

    1. Data Transfer Object로 화면에 필요한 데이터만 있는 오브젝트
    1. LogResponse는 log_tb 컬럼만 있거나 store_tb 컬럼만 있는 게 아니라 log list에서 필요한, 응답되는 데이터만 담은 클래스이다.
    1. ResponseDTO 클래스를 페이지마다 만드는 것보다는 LogResponse라는 전체 log에 대한 응답 클래스 안에 각 Log와 관련된 페이지 마다의 DTO를 만드는 것이 관리하기에 용이하다.
      1. LogResponse.ListPage listPage = new LogResponse.ListPage();
      2. LogResponse.ListPage로 접근한다.
     

    2. LogResponse.ListPage

    1. ListPage 클래스는 LogRepository에서 join 쿼리로 조회한 컬럼들을 필드로 한다.
    1. @Data : 내부에 getter,setter,toString 등을 담고 있다.
    1. @AllArgsConstructor : 모든 필드를 파라미터로 갖는 생성자를 자동 생성한다.
    package com.metacoding.storev1.log; import lombok.AllArgsConstructor; import lombok.Data; // DTO : Data Transfer Object -> 화면에 필요한 데이터만 있는 오브젝트 public class LogResponse { // log list에서 응답되는 데이터만 담은 거 // LogResponse.DTO로 접근 -> 관리 용이 // 예) LogResponse.DTO dto = new LogResponse.DTO(); @AllArgsConstructor @Data // 내부에 getter,setter,toString을 담고 있음 public static class ListPage{ // new 3번해서 collection에 담는다? private int id; private String name; private int qty; private int totalPrice; private String buyer; // public ListPage(int id, String name, int qty, int totalPrice, String buyer) { // this.id = id; // this.name = name; // this.qty = qty; // this.totalPrice = totalPrice; // this.buyer = buyer; // } // //toString() // @Override // public String toString() { // return "ListPage [id=" + id + ", name=" + name + ", qty=" + qty + ", totalPrice=" + totalPrice + ", buyer=" + buyer + "]"; // } } }
     

    4. 다시 LogRepository를 완성하러 가자

    1. 쿼리를 실행한 결과를 담은 obsList 오브젝트 배열 타입의 리스트를 for문으로 돌면서 오브젝트 배열을 하나씩 가져온다.
    1. 그리고 배열의 0번지, 1번지, 2번지, 3번지인 lt.id, st.name, lt.qty, lt.total_price, lt.buyer를 LogResponse.ListPage 생성자의 인수로 받아서 LogResponse.ListPage 객체를 만든다.
    1. 여러 개의 LogResponse.ListPage 객체를 List<LogResponse.ListPage> logList에 담는다.
    1. 따라서 logList를 리턴하면 join 쿼리를 실행한 모든 행을 가져올 수 있다.
    public List<LogResponse.ListPage> findAllJoinStore() { List<LogResponse.ListPage> logList = new ArrayList<>(); String q = "SELECT lt.id, st.name, lt.qty, lt.total_price, lt.buyer FROM log_tb lt INNER JOIN store_tb st ON lt.store_id = st.id ORDER BY lt.id DESC"; Query query = em.createNativeQuery(q); List<Object[]> obsList = (List<Object[]>) query.getResultList(); // Object[] -> ROW ([id, name, qyt, total_price, buyer]) for (Object[] obs : obsList) { LogResponse.ListPage log = new LogResponse.ListPage( (int) obs[0], (String) obs[1], (int) obs[2], (int) obs[3], (String) obs[4]); logList.add(log); } return logList; }
     

    5. LogController를 만들자

    1. 구매 목록 페이지에서는 repository에서 service, service에서 controller로 전달된 구매 목록 데이터들을 request에 담아서 화면에 뿌려줘야 된다.
    @Controller public class LogController { private LogService logService; public LogController(LogService logService) { this.logService = logService; } @GetMapping("/log") public String list(HttpServletRequest request) { List<LogResponse.ListPage> listPage = logService.구매목록(); // dto를 받음 (model은 테이블과 똑같은 것) request.setAttribute("models", listPage); return "log/list"; } }
     

    6. LogService 만들기

    1. LogService 클래스에서는 LogRepository 클래스에서 받아온 데이터를 LogController로 넘겨주는 책임만 가진다.
    @Service public class LogService { private LogRepository logRepository; private StoreRepository storeRepository; public LogService(LogRepository logRepository, StoreRepository storeRepository) { this.logRepository = logRepository; this.storeRepository = storeRepository; } public List<ListPage> 구매목록() { return logRepository.findAllJoinStore(); } }
     

    7. log/list.mustache 완성하기

    1. models라는 key를 통해 반복문을 돌면서 id, name, qty, totalPrice, buyer를 가져온다.
    {{>layout/header}} <section> <table border="1"> <tr> <th>주문번호</th> <th>상품명(조인)</th> <th>구매개수</th> <th>총가격</th> <th>구매자이름</th> </tr> {{#models}} <tr> <td>{{id}}</td> <td>{{name}}</td> <td>{{qty}}개</td> <td>{{totalPrice}}원</td> <td>{{buyer}}</td> </tr> {{/models}} </table> </section> </body> </html>
    Share article

    jay0628

    RSS·Powered by Inblog