1. Join 먼저
1. log/list.mustache부터 보자!
- “주문번호/상품명/구매개수/총가격/구매자이름”이 필요하다는 것을 알 수 있다.
- 여기서 바나나는 log_tb를 통해 알 수 있는 것이 아니다
- log_tb에서는 store_id를 알 수 있고 store_id를 통해 상품명을 가져오게 된다.

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

2. LogRepository로 가자!
1. 수동 Object Mapping
- EntityManager는 JPA가 관리해주는 @Entity가 붙어있는 Entity만 매핑 가능하다.
- Entity란 JPA가 관리하는 데이터베이스의 테이블과 매핑되는 자바 객체
- Join한 데이터는 Store.class나 Log.class와 같은 Entity가 아니기 때문에 자동 매핑이 불가능하다.
- createNativeQuery 메서드는 Query 객체를 만들어낸다.
- 쿼리의 실행 결과가 여러 개의 행으로 리스트 형태일 경우 getResultList 메서드를 사용한다.
- Object[]는 한 개의 행을 의미한다.
- Object[] = [Ob, Ob, Ob, Ob, Ob] => [id, name, qyt, total_price, buyer]를 의미한다.
- List<Object[]> = [Ob, Ob, Ob, Ob] [Ob, Ob, Ob, Ob] … -> 하나하나가 row
- 번지로 parsing해서 꺼내야 된다.
- 따라서 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
- Data Transfer Object로 화면에 필요한 데이터만 있는 오브젝트
- LogResponse는 log_tb 컬럼만 있거나 store_tb 컬럼만 있는 게 아니라 log list에서 필요한, 응답되는 데이터만 담은 클래스이다.
- ResponseDTO 클래스를 페이지마다 만드는 것보다는 LogResponse라는 전체 log에 대한 응답 클래스 안에 각 Log와 관련된 페이지 마다의 DTO를 만드는 것이 관리하기에 용이하다.
- LogResponse.ListPage listPage = new LogResponse.ListPage();
- LogResponse.ListPage로 접근한다.
2. LogResponse.ListPage
- ListPage 클래스는 LogRepository에서 join 쿼리로 조회한 컬럼들을 필드로 한다.
- @Data : 내부에 getter,setter,toString 등을 담고 있다.
- @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를 완성하러 가자
- 쿼리를 실행한 결과를 담은 obsList 오브젝트 배열 타입의 리스트를 for문으로 돌면서 오브젝트 배열을 하나씩 가져온다.
- 그리고 배열의 0번지, 1번지, 2번지, 3번지인 lt.id, st.name, lt.qty, lt.total_price, lt.buyer를 LogResponse.ListPage 생성자의 인수로 받아서 LogResponse.ListPage 객체를 만든다.
- 여러 개의 LogResponse.ListPage 객체를 List<LogResponse.ListPage> logList에 담는다.
- 따라서 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를 만들자
- 구매 목록 페이지에서는 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 만들기
- 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 완성하기
- 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