java, spring

[Spring] 영속 영역의 CRUD 구현 (MyBatis - CRUD)

isaac.kim 2021. 6. 21. 23:53
728x90
반응형

[Spring] 영속 영역의 CRUD 구현 (MyBatis - CRUD)

이번 글에서는 MyBatis에서 CRUD를 구현해봅니다. 이전 글에서 CRUD를 구현하기 전까지의 과정을 다루었으니 필요하다면 참고하시기 바랍니다.

 

https://lifere.tistory.com/138

 

[Spring] 영속 계층의 CRUD 구현을 위한 환경설정

[Spring] 영속 계층의 CRUD 구현을 위한 환경설정 영속 계층의 작업은 항상 다음과 같은 순서로 진행합니다. 1. 테이블의 컬럼 구조를 반영하는 VO(Value Object) 클래스의 생성 2. MyBatis의 Mapper 인터페이

lifere.tistory.com

 

MyBatis는 내부적으로 JDBC의 PreparedStatement를 활용하고 필요한 파라미터를 처리하는 '?'에 대한 치환은 '#{속성}'을 이용해서 처리합니다.

 

1. create(insert) 처리

tb_board 테이블은 PK 컬럼으로 seq_bno를 이용하고, 이 컬럼은 자동으로 시퀀스 번호가 부여되는 방법을 사용해 데이터가 추가될 때 번호가 만들어지는 방식을 사용합니다. 

 

MariaDB 오토 시퀀스 관련 내용 확인하기 ▼

https://lifere.tistory.com/130

 

[MariaDB] MariaDB 테이블 생성, 오토 시퀀스 (자동 값 증가)

[MariaDB] MariaDB 테이블 생성, 오토 시퀀스 (자동 값 증가) 테이블 생성하기 CREATE TABLE tb_board ( seq_bno INT NOT NULL AUTO_INCREMENT, title VARCHAR(255) NOT NULL, content VARCHAR(2000) NOT NULL, wr..

lifere.tistory.com

 

이처럼 자동으로 PK 값이 정해지는 경우에는 다음과 같은 2가지 방식으로 처리할 수 있습니다.

 

* insert만 처리되고 생성된 PK 값을 알 필요가 없는 경우

* insert문이 실행되고 생성된 PK 값을 알아야 하는 경우,

혹은 현재 생성되어 있는 마지막 키 값을 알고 싶은 경우

 

BoardMapper 인터페이스에는 위 상황들을 고려해서 다음과 같이 메서드를 추가 선언합니다.

insert 메서드, insertSelectKey 메서드

package com.project.mapper;

import java.util.List;

import org.apache.ibatis.annotations.Select;

import com.project.domain.BoardVO;

public interface BoardMapper {
	
	//@Select("SELECT * FROM tb_board WHERE seq_bno > 0")
	public List<BoardVO> getList();
	
	public void insert(BoardVO board);
	
	public void insertSelectKey(BoardVO board);
}

 

BoardMapper.xml 은 다음과 같이 내용을 추가합니다.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
	PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.project.mapper.BoardMapper">

	<select id="getList" resultType="com.project.domain.BoardVO">
	<![CDATA[
	select * from tb_board where seq_bno > 0
	]]>
	</select>

	<insert id="insert">
	insert into tb_board (TITLE, CONTENT, WRITER)
	values (#{title}, #{content}, #{writer})
	</insert>

	<insert id="insertSelectKey">
		<selectKey keyProperty="seq_bno" order="BEFORE"
			resultType="long">
			SELECT max(seq_bno) + 1 FROM tb_board 
		</selectKey>
		INSERT INTO tb_board (SEQ_BNO, TITLE, CONTENT, WRITER)
		VALUES (#{seq_bno}, #{title}, #{content}, #{writer})
	</insert>

</mapper>

insertSelectKey( ) 는 @SelectKey라는 MyBatis의 어노테이션을 이용합니다. @SelectKey는 주로 PK 값을 미리(before) SQL을 통해서 처리해 두고 특정한 이름으로 결과를 보관하는 방식입니다. @Insert 할 때 SQL문을 보면 #{seq_bno} 와 같이 이미 처리된 결과를 이용하는 것을 볼 수 있습니다.

 

TEST

insert()에 대한 테스트 코드를 작성합니다. BoardMapperTests 클래스에 새로운 메서드로 작성해보면 다음과 같이 작성할 수 있습니다.

	@Test
	public void testInert() {
		BoardVO board = new BoardVO();
		board.setTitle("새로 작성하는 글");
		board.setContent("새로 작성하는 내용");
		board.setWriter("newbie");
		
		mapper.insert(board);
		
		log.info(board);
	}

테스트 코드의 마지막에 log.info(board)를 작성한 이유는 Lombok이 만들어주는 toString( )을 이용해서 seq_bno 멤버 변수(인스턴스 변수)의 값을 알아보기 위함입니다. 

seq_bno는 null로 표기되어 있으나 데이터베이스 상에 insert될 때 컬럼 seq_bno는 오토 시퀀스로 값이 증가하기 때문에 자동으로 값이 들어가 있습니다.

위에서 직접 값을 넣은 title, content, writer와 regdate, updatedate 그리고 제일 앞의 seq_bno는 오토시퀀스로 자동으로 값이 들어가 있는 것을 확인할 수 있습니다.

 

@SelectKey를 이용하는 경우 테스트 코드

테스트 코드 실행 결과

 

insert 쿼리가 실행되기 전 먼저 실행된 selectKey 쿼리 실행결과

selectKey 쿼리가 먼저 실행되고 여기서 생성된 결과를 이용해서 seq_bno 값으로 insert 문의 #{seq_bno} 값으로 사용되는 것을 확인할 수 있습니다.

 

insert 쿼리

쿼리를 보면 4개의 값(?, ?, ?, ?)을 입력하는데 테스트 코드에서 세팅하지 않은 SEQ_BNO의 값에는 먼저 실행된 selectKey 쿼리 결과인 4가 입력되어 insert 쿼리를 실행하는 것을 확인할 수 있습니다.

 

결과 : 테이블 확인

제일 최근 입력된 테이블 seq_bno에 4가 들어가 있는 것을 확인할 수 있습니다. 

 

 

BoardMapper의 insertSelectKey( )의 @Insert 문의 SQL을 보면 'INSERT INTO tb_board (SEQ_BNO, TITLE, CONTENT, WRITER)

VALUES (#{seq_bno}, #{title}, #{content}, #{writer})' 와 같이 파라미터로 전달되는 BoardVO의 seq_bno 값을 사용하게 되어 있습니다. 

 

 @SelectKey를 이용하는 방식은 SQL을 한 번 더 실행하는 부담이 있기는 하지만 자동으로 추가되는 PK값을 확인해야 하는 상황에서는 유용하게 사용될 수 있습니다.

 

2. read(select) 처리

insert가 된 데이터를 조회하는 작업은 PK를 이용해서 처리하므로 BoardMapper의 파라미터 역시 BoardVO 클래스의 seq_bno 타입 정보를 이용해서 처리합니다.

 

BoardMapper.java 인터페이스에 read 메서드 추가

BoardMapper.xml 에 select 쿼리 문 추가

MyBatis는 seq_bno라는 칼럼이 존재하면 인스턴스의 'setSeq_bno( )를 호출하게 됩니다. MyBatis의 모든 파라미터의 리턴 타입의 처리는 get 파라미터명( ), set 칼럼명( )의 규칙으로 호출됩니다. 다만 위와 같이 #{속성}이 1개만 존재하는 경우에는 별도의 get 파라미터명( )을 사용하지 않고 처리됩니다.

 

현재 존재하는 데이터를 조회해야 하므로 위에서 insert 했던 (seq_bno가 4인)데이터를 조회하는 테스트코드를 작성합니다.

실행 결과

 

3. delete 처리

특정 데이터를 삭제하는 작업 역시 PK 값을 이용해서 처리하므로 조회하는 작업과 유사하게 처리합니다. 등록, 삭제, 수정과 같은 DML 작업은 '몇 건의 데이터가 삭제(혹은 수정)되었는지'를 반환할 수 있습니다.

 

BoardMapper 인터페이스에 메서드 추가

BoardMapper.xml 에 코드 추가

delete( )의 메서드 리턴 타입은 int로 지정해서 만일 정상적으로 데이터가 삭제되면 1 이상의 값을 가지도록 작성합니다. 테스트 코드는 현재 테이블에 존재하는 번호의 데이터를 삭제해 보고 '1'이라는 값이 출력되는지 확인합니다. 만일 해당 번호의 게시물이 없다면 '0'이 출력됩니다.

 

테스트 코드 작성

BoardMapperTests 클래스에 메서드 추가

실행 결과

테이블에 seq_bno가 4인 데이터가 삭제된 것을 확인할 수 있습니다.

 

4. update 처리

마지막으로 update를 처리합니다. 게시물의 업데이트는 제목, 내용, 작성자를 수정한다고 가정합니다. 업데이트할 때는 최종 수정시간을 데이터베이스 내 현재 시간으로 수정합니다. Update는 delete와 마찬가지로 '몇 개의 데이터가 수정되었는가'를 처리할 수 있게 int 타입으로 메서드를 설계할 수 있습니다.

 

BoardMapper interface에 update 메서드 추가

BoardMapper.xml에 update 쿼리문 추가 (둘 다 사용 가능)

쿼리 실행 전 테이블

테스트 코드 작성

테스트 코드 실행 및 결과

쿼리 실행 후 테이블

seq_bno가 1인 데이터가 변경된 것을 확인할 수 있습니다. SQL에서 update 컬럼이 최종 수정시간을 의미하는 칼럼이기 때문에 현재 시간으로 변경해 주고 있다는 점과, regdate 칼럼은 최초 생성 시간이므로 건드리지 않는다는 점입니다.

#{title}과 같은 부분은 파라미터로 전달된 BoardVO 객체의 getTitle( )과 같은 메서드들을 호출해서 파라미터들이 처리됩니다.


이렇게 해서 Spring에서 영속 영역의 CRUD 구현 (MyBatis - CRUD) 에 대해서 알아보았습니다.

728x90
반응형