본문 바로가기

Spring

ch04 02. Mybatis로 DAO 작성하기

1. BoardDao의 작성


1) DB테이블 생성

 

2) Mapper XML & DTO 작성

 <mapper namespace="com.fastcampus.ch4.dao.BoardMapper">
 <select id ="select" parameterType="int" resultType="BoardDto">
 		SELECT bno, title, content, writer, view_cnt, comment_cnt, reg_date
		FROM board
        WHERE bno = #{bno}
    </select>

DB테이블로부터 CRUD하기 위한 SQL문들을 작성해야한다. 

 

3) DAO 인터페이스 작성

public interface BoardDao {
	BoardDto select(Integer bno) throws Exception;
    int delete(Integer bno) throws Exception;
    int insert(BoardDto bno) throws Exception;
    int update(BoardDto bno) throws Exception;
    int increaseViewCnt(Integer bno) throws Exception;
}

4) DAO 인터페이스 구현 & 테스트

@Repository
public class BoardDaoImpl implements BoardDao {
    @Autowired
    private SqlSession session;
    private static String namespace = "com.fastcampus.ch4.dao.BoardMapper.";

    public BoardDto select(Integer bno) throws Exception {
        return session.selectOne(namespace + "select", bno);
    } // T selectOne(String statement, Object parameter)

 

 

2. DTO란? Data Transfer Object 

- 계층간의 데이터를 주고 받기 위해 사용되는 객체 

create table board {
	bno			int auto_increment		primary key,
    title		varchar(45)				not null,
    content		text					not null,
    writer		varchar(30)				not null,
    view_cnt	int default 0			not null,
    comment_cnt	int default 0				null,
    reg_date	datetime					null
};
public class BoardDto {
	private Integer bno; // 게시물 번호
	private String title;
	private String content;
	private String writer;
	private int view_cnt;
	private int comment_cnt;
	private Date reg_date;
    
    // 생성자, getter & setter, toString()
    // ... 
}

게시판에서 글을 읽을 때 DB 테이블에 있는 데이터를 BoardDto라는 객체에 담아서 가져와야하고, 

게시판에 글을 쓸 때는 Dto라는 객체에 값을 채워서 DB에 있는 Board 테이블에 저장하는 것이다. 

게시물 번호는 INTEGER로 설정하였는데, 변환 에러를 피하기 위해 int 대신 사용했다.

 

사용자가 요청한 데이터를 DTO에 담아서 Service 계층 -> Repository 계층으로 데이터를 전달할 필요가 있다.

계층으로 분리를 해놓았기 때문에 각 계층간에 데이터를 전달할 때 사용하는게 DTO이다. 

해당 데이터가 DB에 저장되고, 조회를 하면 그 조회를 한 데이터를 DTO에 담아서 각 계층을 거쳐서 응답하게

되는 것이다. 

@Repository 계층 DAO에서는 예외처리를 하지 않고, 예외를 던진다.

@Service 계층에서 던진 예외를 받으면 예외는 @Controller와 @Service가 둘 다 처리한다.(예외 되던지기)
@Service에서 예외를 처리하면 @Controller는 해당 예외를 알 수 없으므로, @Service가 예외의 일부는 
처리하고 다시 예외를 던져서 @Controller에서도 예외를 처리할 수도 있다. 

@Service, @Controller, 둘 다 처리 중 어떻게 예외를 처리할지는 고민 사항이며, 

복잡하면 @Controller로 넘겨서 처리하자!

 

- BoardDao, BoardDaoImpl, BoardDaoImplTest, boardMapper.xml 생성

BoardDao
BoardDaoImpl
BoardDaoImplTest

테스트가 통과하고 null이 아닌 값이 나와 결과가 잘 나온 것을 확인할 수 있다. 

 

다음은 게시물 번호가 일치하면, 

board 컬럼에 맞게 열을 하나 삽입한 후 게시물 넘버가 일치하면 boardDto가 출력되도록 테스트를 수행한다. 

테스트가 통과되면 Dao도 잘 주입되었고, DB에 있는 내용을 잘 가져온 걸 확인할 수 있다. 

 

 

3. #{} ${}의 차이

<insert id="insert" parameterType="BoardDto">
	INSERT INTO board
    	(title, content, writer)
    VALUES
    	(#{title}, #{content}, #{writer})
 </insert>
String sql = "INSERT INTO board"
		+ "(title, content, writer)"
        + "VALUES"
        + "(?,?,?)";

PreparedStatement pstmt = conn.prepareStatement(sql);
int result = pstmt.executeUpdate();

PreparedStatement를 쓰도록 바뀌고 ?로 바뀐다. 

PreparedStatement는 sql문에서 오로지 들어갈 값들에만 사용할 수 있다. 

#으로 시작하는건 PreparedStatement를 이용. 

타입에 따라서 자동으로 문자라면 ""를 붙여준다. 

외부에서 넘어온 값을 사용할 때 SQL Injection과 같은 상황을 막아준다. 값에만 사용할 수 있으니까! 

 

<insert id="insert" parameterType="BoardDto">
	INSERT INTO board
    	(title, content, writer)
    VALUES
    	('&{title}', '&{content}', '&{writer}')
 </insert>
String sql = "INSERT INTO board"
		+ "(title, content, writer)"
        + "VALUES"
        + "('"title+"','"+content+"','"writer"')";

Statement stmt = conn.createStatement(sql);
int result = stmt.executeUpdate(sql);

일반 Statement로 바뀐다. 

여기선 ''를 사용해야한다. 일반 Statement를 사용하기 때문에 직접 문자열을 구성하도록 되어있다. 
이게 더 유연하고 제약이 적다. 테이블 이름 board 대신에도 ${table}이런식으로 동적으로 바꿀 수 있다.

(#은 값만 사용 가능)

SQL Injection방지에 취약하다. 

 

 

가능하면 #을 사용하지만, 동적으로 SQL을 구성할 때는 $도 필요하다. 

그래서 $는 내부적으로만 사용하고, $일 때 외부에서 입력받은 값을 사용할 땐 철저하게 검증해서 사용해야 한다. 

#을 기본으로 쓰고 잘 안되면 $으로 바꿔서 사용해보자. 

 

 

4. XML의 특수 문자 처리

- XML내의 특수 문자(<,>,&,...)는 &lt; &gt;로 변환 필요

- 또는 특수문자가 포함된 쿼리를 <![CDATA[ ]]>로 감싼다. 

<update id="update" parameterType="BoardDto">
	UPDATE board
    SET title = #{title}
     , 	content = #{content}
     , 	up_date = now()
    WHERE bno = #{bno} and bno &lt;&gt; 0
</update>

<> 이런것들은 태그로 사용하는 문자이기 때문에 들어가 있으면 에러가 날 수 있다. 
<가 < , >가 >이며, 이를 변환해서 사용해줘야 한다. 

<update id="update" parameterType="BoardDto">
	<![CDATA[
    UPDATE board
    SET title = #{title}
     , 	content = #{content}
     , 	up_date = now()
    WHERE bno = #{bno} and bno <> 0
    ]]>
</update>

SQL문은 &lt, &gt를 알아보기 힘들고, 변환해야 하는 문자들이 많을 때는 <![CDATA[ ]]> 전체를 이 태그로 감싸면 된다.

그러면 이 안에서는 태그가 없다는 뜻이다.

CDATA -> 전부 문자 데이터