Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
Tags
- baeldung
- resttemplate
- 네트워크 설정
- GIT
- 자료구조와함께배우는알고리즘입문
- 자바편
- Kernighan의 C언어 프로그래밍
- 선형대수
- 티스토리 쿠키 삭제
- 처음 만나는 AI수학 with Python
- 코드로배우는스프링부트웹프로젝트
- 이터레이터
- 친절한SQL튜닝
- 데비안
- 서버설정
- 목록처리
- 리눅스
- 알파회계
- ㅒ
- network configuration
- 스프링 시큐리티
- 구멍가게코딩단
- 코드로배우는스프링웹프로젝트
- iterator
- d
- /etc/network/interfaces
- 스프링부트핵심가이드
- 처음 만나는 AI 수학 with Python
- 페이징
- 자료구조와 함께 배우는 알고리즘 입문
Archives
- Today
- Total
bright jazz music
[bootBoard] N:1(다대일) 연관관계: 9-2. 프로젝트 적용: 게시물 목록 처리 본문
Framework/Spring
[bootBoard] N:1(다대일) 연관관계: 9-2. 프로젝트 적용: 게시물 목록 처리
bright jazz music 2022. 10. 5. 22:311. 게시물 목록 처리: PageRequestDTO와 PageResultDTO 추가
목록을 처리하기 위해 PageRequestDTO와 PageResultDTO 추가
PageRequestDTO.java
//PageRequestDTO.java
package com.example.bootboard.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable; //페이지 관련 임포트
import org.springframework.data.domain.Sort;
@Builder
@AllArgsConstructor
@Data
public class PageRequestDTO {
private int page;
private int size;
//검색을 위해 추가
private String type;
private String keyword;
public PageRequestDTO(){
this.page = 1;
this.size = 10;
}
// 이 DTO의 목적은 JPA에서 사용하는 Pageable 타입의 객체를 생성하는 것이다.
// JPA를 이용하는 경우에는 페이지 번호가 0부터 시작한다.
// 따라서 1페이지의 경우 0이 될 수 있도록 아래와 같이 작성한다.
// 정렬(Sort)는 다양한 상황에서 사용하기 위해서 별도의 파라미터로 받도록 설계.
public Pageable getPageable(Sort sort) {
return PageRequest.of(page - 1, size, sort );
}
}
PageResultDTO.java
//PageResultDTO.java
package com.example.bootboard.dto;
import lombok.Data;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@Data
public class PageResultDTO<DTO, EN> {
// 다양한 곳에서 사용할 수 있도록 제네릭 타입을 이용해서 DTO와 EN 타입을 지정. (EN = Entity)
//DTO리스트
private List<DTO> dtoList;
//총 페이지 번호
private int totalPage;
//현재 페이지 번호
private int page;
//목록 사이즈
private int size;
//시작 페이지 번호, 끝 페이지 번호
private int start, end;
//이전, 다음
private boolean prev, next;
//페이지 번호 목록
private List<Integer> pageList;
//PageResultDTO는 Page<Entity> 타입을 이용해서 생성할 수 있도록 생성자로 작성.
public PageResultDTO(Page<EN> result, Function<EN, DTO> fn){
//Function: 엔티티 객체들을 DTO로 변환해 주는 기능
dtoList = result.stream().map(fn).collect(Collectors.toList());
totalPage = result.getTotalPages();
makePageList(result.getPageable());
}
private void makePageList(Pageable pageable){
this.page = pageable.getPageNumber() + 1; //0부터 시작하므로 1추가
this.size = pageable.getPageSize();
//temp end page
int tempEnd = (int)(Math.ceil(page/10.0)) * 10;
start = tempEnd - 9;
prev = start > 1;
end = totalPage > tempEnd ? tempEnd : totalPage;
next = totalPage > tempEnd;
pageList = IntStream.rangeClosed(start, end).boxed()
.collect(Collectors.toList());
}
// 이와 같은 제네릭 방식을 적용하면 추후 추가적인 클래스를 작성하지 않고도 목록 데이터를 처리할 수 있다.
/*
* PageResultDTO는 List<DTO> 타입으로 DTO 객체들을 보관한다.
* 그렇기 때문에 Page<Entity>의 내용물 중에서 엔티티 객체를 DTO로 변환하는 기능이 필요하다.
* 가장 일반적인 형태는 추상 클래스를 이용해서 이를 처리하는 방식이다.
* 그러한 경우 매번 새로운 클래스가 필요하다는 단점이 있다.
*
* 이번 프로젝트의 경우 엔티티 객체의 DTO 변환은 서비스 인터페이스에 정의한
* entityToDto() 메서드와 별도로 Function객체로 만들어서 처리한다.
* */
}
- PageResultDTO의 핵심은 JPQL로 나오는 Object[ ]를 DTO타입으로 변환하는 기능이다.
- 이 기능은 java.util.Function을 이용해 작성한다.
- 현재 예제의 경우 JPQL의 실행 결과로 나오는 Object[ ]을 BoardDTO로 처리해 주어야 한다.
Object[ ]의 내용은 Board와 Member, 댓글의 수는 Long 타입으로 나온다. 따라서 이를 전달받아서 BoardDTO를 구성하도록 작성해야 한다.
이 기능은 BoardService 인터페이스에 entityToDTO()라는 메서드를 작성하여 처리한다.
2. BoardService에 entityToDTO()와 getList() 추가
- entityToDTO()는 3개의 파라미터를 가진다. 그리고 이 파라미터들의 값을 이용해서 boardDTO 객체를 생성한다.
- 게시물의 목록처리를 위해 getList()라는 메서드를 만들어 사용한다.
//BoardService.java
package com.example.bootboard.service;
import com.example.bootboard.dto.BoardDTO;
import com.example.bootboard.dto.PageRequestDTO;
import com.example.bootboard.dto.PageResultDTO;
import com.example.bootboard.entity.Board;
import com.example.bootboard.entity.Member;
public interface BoardService {
Long register(BoardDTO dto);
//getList() : 목록처리
PageResultDTO<BoardDTO, Object[]> getList(PageRequestDTO pageRequestDTO);
default Board dtoToEntity(BoardDTO dto) {
Member member = Member.builder().email(dto.getWriterEmail()).build();
Board board = Board.builder()
.bno(dto.getBno())
.title(dto.getTitle())
.content(dto.getContent())
.writer(member)
.build();
return board;
}
//entityToDTO()
default BoardDTO entityToDTO(Board board, Member member, Long replyCount) {
BoardDTO boardDTO = BoardDTO.builder()
.bno(board.getBno())
.title(board.getTitle())
.content(board.getContent())
.regDate(board.getRegDate())
.modDate(board.getModDate())
.writerEmail(member.getEmail())
.writerName(member.getName())
.replyCount(replyCount.intValue()) //long으로 나오므로 int처리
.build();
return boardDTO;
}
}
BoardService 인터페이스에서 선언해줬으니 BoardServiceImpl에 오버라이드 필요.
3. BoardServiceImpl 수정(BoardService 인터페이스에 맞춰 오버라이드)
BoardServiceImpl.java
//BoardServiceImpl.java
package com.example.bootboard.service;
import com.example.bootboard.dto.BoardDTO;
import com.example.bootboard.dto.PageRequestDTO;
import com.example.bootboard.dto.PageResultDTO;
import com.example.bootboard.entity.Board;
import com.example.bootboard.entity.Member;
import com.example.bootboard.repository.BoardRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import java.util.function.Function;
@Service
@RequiredArgsConstructor
@Log4j2
public class BoardServiceImpl implements BoardService {
private final BoardRepository repository; // 자동 주입 final
@Override
public Long register(BoardDTO dto) {
log.info(dto);
Board board = dtoToEntity(dto);
repository.save(board);
return board.getBno();
}
//페이지 목록 가져오는 getList()
@Override
public PageResultDTO<BoardDTO, Object[]> getList(PageRequestDTO pageRequestDTO) {
log.info(pageRequestDTO);
Function<Object[], BoardDTO> fn =
(en -> entityToDTO((Board)en[0], (Member)en[1], (Long)en[2]));
//Board board, Member member, Long replyCount
Page<Object[]> result = repository.getBoardWithReplyCount(
pageRequestDTO.getPageable(Sort.by("bno").descending()));
return new PageResultDTO<>(result, fn);
}
}
4. BoardServiceTests에 테스트 코드 추가
//BoardServiceTests.java
...
@Test
public void testList() {
PageRequestDTO pageRequestDTO = new PageRequestDTO();
PageResultDTO<BoardDTO, Object[]> result = boardService.getList(pageRequestDTO);
for(BoardDTO boardDTO : result.getDtoList()){
System.out.println(boardDTO);
}
}
...
5. 테스트 결과
BoardDTO 객체 내에 목록 화면에 필요한 10개의 BoardDTO 객체가 만들어진다. 이 DTO에는 bno, title, content 등의 내용이 담겨 있다.
2022-10-06 22:57:02.347 INFO 27388 --- [ Test worker] c.e.bootboard.service.BoardServiceTests : Started BoardServiceTests in 3.861 seconds (JVM running for 5.397)
2022-10-06 22:57:02.486 INFO 27388 --- [ Test worker] c.e.bootboard.service.BoardServiceImpl : PageRequestDTO(page=1, size=10, type=null, keyword=null)
Hibernate:
select
board0_.bno as col_0_0_,
member1_.email as col_1_0_,
count(reply2_.rno) as col_2_0_,
board0_.bno as bno1_0_0_,
member1_.email as email1_1_1_,
board0_.moddate as moddate2_0_0_,
board0_.reg_date as reg_date3_0_0_,
board0_.content as content4_0_0_,
board0_.title as title5_0_0_,
board0_.writer_email as writer_e6_0_0_,
member1_.moddate as moddate2_1_1_,
member1_.reg_date as reg_date3_1_1_,
member1_.name as name4_1_1_,
member1_.password as password5_1_1_
from
board board0_
left outer join
member member1_
on board0_.writer_email=member1_.email
left outer join
reply reply2_
on (
reply2_.board_bno=board0_.bno
)
group by
board0_.bno
order by
board0_.bno desc limit ?
Hibernate:
select
count(board0_.bno) as col_0_0_
from
board board0_
BoardDTO(bno=103, title=Test., content=Test ..., writerEmail=user55@aaa.com, writerName=USER55, regDate=2022-10-05T21:09:22.659477, modDate=2022-10-05T21:09:22.659477, replyCount=0)
BoardDTO(bno=102, title=Title...100, content=Content...100, writerEmail=user100@aaa.com, writerName=USER100, regDate=2022-10-05T21:01:48.365059, modDate=2022-10-05T21:01:48.365059, replyCount=0)
BoardDTO(bno=101, title=Title...99, content=Content...99, writerEmail=user99@aaa.com, writerName=USER99, regDate=2022-10-05T21:01:48.359115, modDate=2022-10-05T21:01:48.359115, replyCount=0)
BoardDTO(bno=100, title=Title...98, content=Content...98, writerEmail=user98@aaa.com, writerName=USER98, regDate=2022-10-05T21:01:48.353275, modDate=2022-10-05T21:01:48.353275, replyCount=1)
BoardDTO(bno=99, title=Title...97, content=Content...97, writerEmail=user97@aaa.com, writerName=USER97, regDate=2022-10-05T21:01:48.347276, modDate=2022-10-05T21:01:48.347276, replyCount=1)
BoardDTO(bno=98, title=Title...96, content=Content...96, writerEmail=user96@aaa.com, writerName=USER96, regDate=2022-10-05T21:01:48.340790, modDate=2022-10-05T21:01:48.340790, replyCount=1)
BoardDTO(bno=97, title=Title...95, content=Content...95, writerEmail=user95@aaa.com, writerName=USER95, regDate=2022-10-05T21:01:48.335785, modDate=2022-10-05T21:01:48.335785, replyCount=0)
BoardDTO(bno=96, title=Title...94, content=Content...94, writerEmail=user94@aaa.com, writerName=USER94, regDate=2022-10-05T21:01:48.329786, modDate=2022-10-05T21:01:48.329786, replyCount=0)
BoardDTO(bno=95, title=Title...93, content=Content...93, writerEmail=user93@aaa.com, writerName=USER93, regDate=2022-10-05T21:01:48.323767, modDate=2022-10-05T21:01:48.323767, replyCount=0)
BoardDTO(bno=94, title=Title...92, content=Content...92, writerEmail=user92@aaa.com, writerName=USER92, regDate=2022-10-05T21:01:48.316842, modDate=2022-10-05T21:01:48.316842, replyCount=0)
2022-10-06 22:57:02.692 INFO 27388 --- [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2022-10-06 22:57:02.701 INFO 27388 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2022-10-06 22:57:02.713 INFO 27388 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
BUILD SUCCESSFUL in 17s
4 actionable tasks: 3 executed, 1 up-to-date
오후 10:57:03: Task execution finished ':test --tests "com.example.bootboard.service.BoardServiceTests.testList"'.
'Framework > Spring' 카테고리의 다른 글
[bootBoard] N:1(다대일) 연관관계: 9-5. 프로젝트 적용: 게시물 수정 처리 (0) | 2022.10.08 |
---|---|
[bootBoard] N:1(다대일) 연관관계: 9-4. 프로젝트 적용: 게시물 삭제 처리 (0) | 2022.10.07 |
[bootBoard] N:1(다대일) 연관관계: 9-1. 프로젝트 적용: 게시물 등록 처리 (0) | 2022.10.04 |
[bootBoard] N:1(다대일) 연관관계: 8-2. 조회 화면에 필요한 JPQL 생성 (0) | 2022.09.29 |
[bootBoard] N:1(다대일) 연관관계: 8-1. 목록화면에 필요한 JPQL 생성 (0) | 2022.09.28 |
Comments