해당 프로젝트는 코드로 배우는 스프링 웹 프로젝트(개정판)을 기반으로 진행됩니다.
게시글에 달린 댓글의 개수를 나타내기 위해 댓글 추가시, replyCnt(댓글 개수) 컬럼을 update하는 예제를 진행해본다.
먼저, tbl_board 테이블에 replyCnt 컬럼을 추가한다. (기본 값 0)
alter table tbl_board add (replyCnt number default 0);
기존에 댓글이 존재했다면, replyCnt에 반영해 두어야 하므로 아래의 쿼리를 실행한다.
update tbl_board set replyCnt = (select count(rno)
from tbl_reply
where tbl_reply.bno = tbl_board.bno);
적용 결과
20.1 프로젝트 수정
위와 같이 테이블을 수정하였으므로, BoardVO 클래스와 MyBatis의 SQL, BoardService 등을 수정할 필요가 있다.
20.1.1 BoardVO, BoardMApper 수정
BoardVO 클래스
package org.zerock.domain;
import java.util.Date;
import lombok.Data;
@Data
public class BoardVO {
private Long bno;
private String title;
private String content;
private String writer;
private Date cdate;
private Date udate;
private int replyCnt;
}
BoardMapper 인터페이스
BoardMapper 인터페이스에는 새롭게 replyCnt를 업데이트하는 메소드를 추가해야 한다.
//게시글 수정 : 특정 게시글 수정하기
//수정하고자하는 게시글이 있다면 1, 없으면 0 반환
public int modify(BoardVO board);
//게시글의 전체 개수 구하기
public int getTotalCount(Criteria cri);
//게시글에 대한 댓글 수 업데이트
public void updateReplyCnt(@Param("bno") Long bno, @Param("amount") int amount);
새롭게 추가된 updateReplyCnt()는 해당 게시물의 번호인 bno와 증가나 감소는 의미하는 amount 변수에 파라미터를 받을 수 있도록 처리한다. 댓글이 등록되면 1증가, 삭제되면 1감소한다.
MyBatis의 SQL을 처리할 때, 2개 이상의 데이터를 전달하려면 @Param이라는 어모테이션을 이용해 처리해야 한다.
BoardMapper.xml의 수정
<!-- 게시글의 페이지 번호와 읽어올 개수에 따라 게시글을 조회하는 함수 -->
<!-- CDATA 섹션은 XML에서 사용할 수 없는 부등호를 사용하기 위함. XML의 경우 '<,>'는 태그로 인식하는데, 이를 막기 위함이다. -->
<select id ="getListWithPaging" resultType="org.zerock.domain.BoardVO">
<![CDATA[
select *
from
(select /*+ INDEX_DESC(tbl_board pk_board) */
rownum rn, bno, title, content, writer, cdate, udate, replyCnt
from tbl_board
where
]]>
<include refid="criteria"></include>
<![CDATA[
rownum <= #{pageNum} * #{amount}
)
where rn > (#{pageNum} - 1) * #{amount}
]]>
</select>
...생략
<update id="updateReplyCnt">
update tbl_board
set replyCnt = replyCnt + #{amount}
where bno = #{bno}
</update>
20.1.2 ReplyServiceImpl의 트랜잭션 처리
ReplyServiceImpl 클래스는 기존에는 ReplyMapper만을 이용했지만, 반정규화 처리가 되면서 BoardMapper를 같이 이용해야하는 상황이다. (댓글의 개수 처리) ReplyServiceImpl에서 댓글의 추가/삭제가되는 상황이면 두 Mapper 모두 사용하며 트랜잭션으로 처리되어야 한다.
ReplyServiceImpl 클래스
import lombok.Setter;
import lombok.extern.log4j.Log4j;
@Service
@Log4j
//@AllArgsConstructor
public class ReplyServiceImpl implements ReplyService{
/*
* ReplyServiceImpl은 ReplyMapper에 의존적인 관계이기 때문에 @Setter를 이용해서 처리하거나,
* 스프링 4.3의 생성자와 자동주입을 이용해서 @AllArgsConstructor으로 대체 가능하다.
*
* 추가적으로 boardMappeer를 이용하면서 자동주입 대신 @Setter를 통한 주입이 이뤄져야 함.
*/
@Setter(onMethod_ = @Autowired) //-> @AllArgsConstructor으로 대체 가능
private ReplyMapper mapper;
@Setter(onMethod_ = @Autowired)
private BoardMapper boardMapper;
//댓글 등록의 경우에는 파라미터로 전달받은 ReplyVO 내에 게시글 번호가 존재하므로 이를 이용해서 댓글을 추가함.
@Transactional
@Override
public int register(ReplyVO vo) {
log.info("register....... " + vo);
boardMapper.updateReplyCnt(vo.getBno(), 1);
return mapper.insert(vo);
}
..생략
//댓글의 삭제는 파라미터가 댓글의 번호인 rno만을 받기 때문에 해당 댓글의 게시물을 알아내는 과정이 필요함.
@Transactional
@Override
public int remove(Long rno) {
log.info("remove....... " + rno);
ReplyVO vo = mapper.read(rno);
boardMapper.updateReplyCnt(vo.getBno(), -1);
return mapper.delete(rno);
}
..생략
20.1.3 화면 수정
게시물의 목록 화면에서는 댓글의 숫자가 출력될 수 있도록 댓글의 숫자를 반영한다.
list.jsp의 일부
<c:forEach items="${list}" var="board">
<tr>
<td><c:out value="${board.bno}"/> </td>
<td>
<a class='move' href='<c:out value="${board.bno}"/>'><c:out value="${board.title}"/>
<b>[ <c:out value="${board.replyCnt}"/> ]</b>
</a>
</td>
<td><c:out value="${board.writer}"/> </td>
<td><fmt:formatDate pattern="yyyy-MM-dd" value="${board.cdate}"/> </td>
<td><fmt:formatDate pattern="yyyy-MM-dd" value="${board.udate}"/> </td>
</tr>
</c:forEach>
실행 결과
다음 장에서는 파일 업로드 처리 기능을 추가해 볼 것이다!