게시판〃(8) MyBatis로 DB 연동

반응형


이제 자바파일의 마지막 패키지인 mybatis 부분으로 넘어왔습니다. mybatis를 하기전에 원래 기존 JDBC 코드의 패턴을 보겠습니다.


기존 JDBC코드는 Connection과 Statement를 통해 쿼리를 전송하면 연결받고 Close하는 방식이였습니다. 거기서 프레임워크화를 하여 캡슐화를 한것이 mybatis입니다. 그래서 JDBC 코드를 간편하게 사용할 수 있고 SQL문장과 프로그래밍 코드가 분리되어 있어 사용하는데 있어 편리합니다. 또한 라이브러리를 통해 매개변수를 전달하고 결과를 추출하는 일이 간단히 처리가능하다네요. 


mybatis-3.2.3.jar


그런 장점때문에 게시판 프로젝트에 MyBatis방식을 사용하게 되었고, 설치는 라이브러리에 mybaits.jar파일만 추가해주면 됩니다. 아주 간단하죠. 첨부파일로 올려놓겠습니다. 


이번 제목 게시판 (8)번에서는 MyBatis 패키지를 만들어 DB연동을 할텐데요. SQL문 설정파일과, 설정된 쿼리를 실행하여 Mybatis 객체를 저장하는 파일, db 프로퍼티파일, BoardManager파일 이렇게 4개의 파일을 만들겁니다.




 db.properties 작성하기


아까 언급했던 4개의 파일들을 board.mybatis 패키지에 

위사진과 같이 만들었습니다.

.java 와 .xml은 많이 만들어봤지만

 .properties파일은 처음 보시죠?

한번 만들어보겠습니다.


1) mybatis 패키지 우클릭후 New → Other를 눌러줍니다.



2) General 영역의 그냥 File이라고 적힌 부분을 찾아

Next를 눌러줍니다.


3) 그리고 이름을 지어주시면 됩니다.

저는 db.properties로 간단하게 적었고 Finish를 누르면 끝~!



1
2
3
4
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/hunit
username=root
password=1234


▲ db.properties 소스코드


4) 소스코드는 위와 같이 적어주세요.

여기서 설정했던 driver / url / username / password는 

아래에서 정리할 sqlmapConfig.xml에서 불러올테니 기억해주시고요.





 BoardManager.java 작성하기


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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package board.mybatis;
 
 
import java.io.IOException;
import java.io.Reader;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
 
import board.dto.BoardDto;
 
public class BoardManager {
 
/*    창구역활을하는 sql세션팩토리빌더로 DB연결
    try부분에 sqlmapConfig은 부트스트랩
    */
    
    private static SqlSessionFactory sqlMapper;
    static{
        try {
            Reader reader = Resources.getResourceAsReader("board.mybatis/sqlmapConfig.xml");
            
            sqlMapper = new SqlSessionFactoryBuilder().build(reader); 
        } catch (IOException err){
            throw new RuntimeException("SQL세션팩토리 인스턴스 생성 실패" + err,err);
        }
    }
        
    public static List<BoardDto> boardList(String keyfield, String keyword){
        List<BoardDto> list = null;
        SqlSession session = sqlMapper.openSession();
        if(keyfield != null && keyword != null && keyfield !="" && keyword !=""){
            Map<String, String> map = new HashMap<String, String> ();
            map.put("keyfield" , keyfield);
            map.put("keyword", keyword);
            list = session.selectList("boardSearch", map);
            session.close();
            return list;
        }else {
            list = session.selectList("boardList");
            session.close();
            return list;
        }
    }
    
    public static BoardDto findBySeq(int seq){
        SqlSession session = sqlMapper.openSession();
        BoardDto board = session.selectOne("findBySeq",seq);
        session.close();
        return board;
    }
    
    public static String preView(int seq){
        SqlSession session = sqlMapper.openSession();
        String content = session.selectOne("preView",seq);
        System.out.println(content);
        session.close();
        return content;
    }
    
    public static void readCount(int seq){
        SqlSession session = sqlMapper.openSession();
        session.update("readCount",seq);
        session.commit();
        session.close();
    }
    
    public static void upPos(){
        SqlSession session = sqlMapper.openSession();
        session.update("upPos");
        session.commit();
        session.close();
    }
    
    public static void insertBoard(BoardDto board){
        SqlSession session = sqlMapper.openSession();
        session.insert("insertBoard",board);
        session.commit();
        session.close();
    }
    
    public static int updateBoard(BoardDto board, String pass){
        SqlSession session = sqlMapper.openSession();
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("board", board);
        map.put("password", pass);
        int result = session.update("updateBoard",map);
        session.commit();
        session.close();
        return result;
    }
    
    public static int deleteBoard(int seq, String storPass){
        SqlSession session = sqlMapper.openSession();
        Map map = new HashMap();
        System.out.println("seq : storPass = " + seq + ":" + storPass);
        map.put("seq", seq);
        map.put("storPass", storPass);
        int result = session.delete("deleteBoard",map);
        session.commit();
        session.close();
        return result;
    }
    
    public static String deleteView(int seq){
        SqlSession session = sqlMapper.openSession();
        String storPass = session.selectOne("deleteView",seq);
        session.close();
        return storPass;
    }
    
    public static void replyboard(BoardDto board){
        SqlSession session = sqlMapper.openSession();
        int result = session.insert("replyBoard", board);
        session.commit();
        session.close();
    }
    
    public static void replyUpPos(BoardDto board){
        SqlSession session = sqlMapper.openSession();
        session.update("replyUpPos",board);
        session.commit();
        session.close();
    }
}


▲ BoardManager.java 소스코드


프로퍼티 파일 만드는법을 먼저 올리느라 순서가 밀렸습니다. 이 BoardManager파일은 게시판 제목 (5)번 DAO를 설정하기 위해 만들어졌습니다. 크게 클래스 이름만 보셔도 BoardDao의 클래스와 똑같은걸 느끼셨을거에요.


[23~32] SqlSessionFactory는 빌더와 같이 사용되는 구문입니다. reader로 mybatis폴더의 sqlmapConfig.xml파일을 읽어들이고, 팩토리 내용이 들어있는 sqlMapper를 build(reader)로 저장합니다. catch는 예외처리를 한 것이고요. 또한 SqlSessionFactory 객체가 마이바티스의 정보를 갖는 특성으로 처음에 한번만 생성합니다. 여러번 중복해서 사용하게 되면, 매번 설정파일을 읽고 파싱하고 객체를 생성해야 하기 때문입니다. try-catch 구문을 쓰는 이유는 정확하게 체크할수 없는 계열로 던지고 있기 때문에 내부적으로 예외가 발생하면 close가 안되서 사용합니다. 



[모든 구문] Sql Session을 이용하여 메소드는 반드시 static 정적필드로 선언해야합니다.




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    public static List<BoardDto> boardList(String keyfield, String keyword){
        List<BoardDto> list = null;
        SqlSession session = sqlMapper.openSession();
        if(keyfield != null && keyword != null && keyfield !="" && keyword !=""){
            Map<String, String> map = new HashMap<String, String> ();
            map.put("keyfield" , keyfield);
            map.put("keyword", keyword);
            list = session.selectList("boardSearch", map);
            session.close();
            return list;
        }else {
            list = session.selectList("boardList");
            session.close();
            return list;
        }
    }
cs

▲  boardList 쿼리

[2] list <BoardDto> list = null;  ▶ list를 초기화해줍니다.

[3] SqlSession session = sqlMapper.openSession();  SQL문을 실행하는 메소드 제공(connection.close까지)

[4] if(keyfield != null ~~~~~~ keyword !="") ▶ null값이나 공백이 없다면 즉, 데이타가 있다면

[5] Map map = new HashMap(); ▶ 이렇게 써도 상관없음

[6~7] map.put("keyfield" , keyfield); ▶ map에 key와 value값을 객체로 저장 / MAP을 사용하는 이유 특정값으로 다른 값을 찾을때 사용하면 아주 유용하다.

[8] list = session.selectList("boardSearch", map); ▶ 검색을 하기 위해 list를 select한다. 

[9] session.close ▶ session을 닫아준다.

[10] return list; ▶ 정보가 담긴 list를 리턴

[11~14] else { list ~~ return list; }  ▶ null값이나 공백이 있으면 "boardList"를 그냥 출력하는 정보를 list로 리턴




1
2
3
4
5
6
    public static BoardDto findBySeq(int seq){
        SqlSession session = sqlMapper.openSession();
        BoardDto board = session.selectOne("findBySeq",seq);
        session.close();
        return board;
    }


▲  findBySeq 쿼리

문장으로 설명하면 SQL문을 실행하는 메소드를 제공 → BoardDto의 정보중 findBySeq를 seq의 값에 저장한 것을 하나만 골라 board에 저장 → session을 닫고 → board를 리턴한다. 이런식으로 쿼리를 작성해주시면 되는데 길이가 길어서 어려울 뿐인지 전부 비슷합니다. 그래서 한가지만 더 설명하고 넘어가겠습니다. 




1
2
3
4
5
6
    public static void insertBoard(BoardDto board){
        SqlSession session = sqlMapper.openSession();
        session.insert("insertBoard",board);
        session.commit();
        session.close();
    }


▲  insertBoard 쿼리

전체 쿼리를 보시면 구문에 session.commit( ); 이 들어있는 곳도 있지만 없는곳도 있습니다. session의 기본은 자동으로 commit이 되지 않기 때문에 쿼리를 한다음 [4번째줄]처럼 적어줘야 하는데요. select는 commit이 필요없기 때문에 select구절에만 쓰지 않았습니다. 





 sqlmapConfig.xml 작성하기


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
29
30
31
32
33
34
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 
    "http://mybatis.org/dtd/mybatis-3-config.dtd">
 
<!--  꼭 순서대로 -->    
<configuration>
 
    <!-- DB 프로필 -->
    <properties resource="board/mybatis/db.properties" />
    
    <!-- 알리아스 설정 -->
    <typeAliases>
        <typeAlias alias="BoardDto" type="board.dto.BoardDto"/>
        <typeAlias alias="PageDto" type="board.dto.PageDto" />
    </typeAliases>
    
    <!-- DB 셋팅 -->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
    
    <!-- SQL -->
    <mappers>
        <mapper resource="board/mybatis/boardMapper.xml"/>
    </mappers>
</configuration>    


▲  sqlmapConfig.xml 소스코드


Mybatis의 특징중 하나는 데이터 매퍼의 기능을 가지고 있는데요. 데이터 매퍼는 자바에서 작성한 DAO와 실제 sql에서 쓰는 db가 다른것을 알아볼수있도록 연결시켜주는 역활을 합니다. 그래서 Mybatis는 sql구문의 파라미터를 DAO에 매핑시키는데 파일 하나만 만들어 놓으면 따로 수정할 필요가 없는 편리함에 그 작업을 해줘야합니다.


그 작업중 하나인 Config파일을 만드는 것부터 시작입니다. Config파일에서는 서블릿의 디스패쳐서블릿처럼 설정정보를 저장하게 되는데 저는 여기서 DB프로필을 설정하고, 알리아스 설정, DB 셋팅, SQL 매퍼를 작성했습니다.


(1) DB 프로필은 맨 처음 db.properties로 driver, url, username, password의 정보가 저장되있습니다. (2) 알리아스 설정은 코드길이를 줄이기 위해 사용하였습니다. (3) DB셋팅은 1번의 DB프로필을 가져와 저장합니다. (4) SQL매퍼는 아래에서 정리할텐데 그 SQL 쿼리문파일의 주소를 받아옵니다. 





 boardMapper.xml 작성하기


이제 자바 리소스에 저장하는 마지막 파일을 작성하게 되었습니다. boardMapper는 SQL구문이 저장된 구문을 Map으로 저장해 sqlmapConfig으로 넘겨주게 됩니다. 여기서는 SQL 쿼리문이 들어가는 파일입니다. 소스코드를 두개 올리게 될텐데 그 이유는 insert부분에서 Oracle은 nextval을 쓰지만 MySql은 Oracle과 MySql 구문은 다른 명령어를 씁니다. 그 부분빼고는 전부 같습니다.


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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<?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="hunit.mybatis.boardMapper">
 
 <!-- resultType:sqlmapConfig.xml에서 알리아스설정때문에 간단히씀 -->
<!--  select 시작 -->
    <select id="boardList" resultType="BoardDto" useCache="true">
        select * from tbl_board order by pos asc
    </select>    
    <select id="boardSearch" parameterType="map" resultType="BoardDto">
        select * from tbl_board where ${keyfield} like '%${keyword}%' order by pos asc
    </select>
    <select id="findBySeq" parameterType="int" resultType="BoardDto">
        select * from tbl_board where seq = #{seq}
    </select>
    <select id="preView" parameterType="int" resultType="String">
        select content from tbl_board where seq = #{seq}
    </select>
<!-- select 끝 -->    
 
<!-- 본문 읽기 시작-->
    <update id="readCount" parameterType="int">
        update tbl_board set count = count + where seq = #{seq}
    </update>
    <update id="upPos">
        update tbl_board set pos = pos + 1
    </update>
<!--  본문 읽기 끝 -->
 
<!-- 글쓰기 시작-->
    <insert id="insertBoard" parameterType="BoardDto">
        insert into tbl_board(seq,name,email,homepage,title,content,password,
        count,regdate,pos,depth) values(seq_num.nextval,#{name},#{email},#{homepage},
        #{title},#{content},#{password},0,sysdate,0,0)
    </insert>    
<!-- 글쓰기 종료 -->    
 
<!-- 글수정 시작 -->
    <update id="updateBoard" parameterType="map">
        update tbl_board set title=#{dto.title}, content=#{dto.content}, email=#{dto.email}, name=#{dto.name}
                where seq=#{dto.seq} and password=#{password} 
    </update>
<!-- 글수정 종료 -->
 
<!-- 글삭제와 넘어가는 화면 -->
    <select id="deleteView" parameterType="int" resultType="String">
        select password from tbl_board where seq=#{seq}
    </select>
    <delete id="deleteBoard" parameterType="map">
        delete from tbl_board where seq=#{seq} and password=#{storPass}
    </delete>
<!-- 글삭제 종료 -->
 
<!-- 답변달기와 넘어가는 화면 -->
    <insert id="replyboard" parameterType="BoardDto">
        insert into tbl_board(seq,name,email,homepage,title,content,password,count,regdate,pos,depth)
                values(seq_num.nextval,#{name},#{email},#{homepage},#{title},#{content},#{password},0,sysdate,#{pos}+1,#{depth}+1)
    </insert>
    <update id="replyUpPos" parameterType="BoardDto">
        update tbl_board set pos = pos + where pos>#{pos}
    </update>
<!-- 답변달기 종료 -->
 
</mapper>
cs

▲  ver.오라클 boardMapper소스코드


오라클은 이대로 복사 붙여넣기 하시면 동작이 아주 잘되지만

저처럼 MySql을 쓰신다면 아래코드를 쓰시기 바랍니다.




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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
<?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="hunit.mybatis.boardMapper">
 
 <!-- resultType:sqlmapConfig.xml에서 알리아스설정때문에 간단히씀 -->
<!--  select 시작 -->
    <select id="boardList" resultType="BoardDto" useCache="true">
        select * from tbl_board order by pos asc
    </select>    
    <select id="boardSearch" parameterType="map" resultType="BoardDto">
        select * from tbl_board where ${keyfield} like '%${keyword}%' order by pos asc
    </select>
    <select id="findBySeq" parameterType="int" resultType="BoardDto">
        select * from tbl_board where seq = #{seq}
    </select>
    <select id="preView" parameterType="int" resultType="String">
        select content from tbl_board where seq = #{seq}
    </select>
<!-- select 끝 -->    
 
<!-- 본문 읽기 시작-->
    <update id="readContentCount" parameterType="int">
        update tbl_board set count = count + 1 where seq = #{seq}
    </update>
    <update id="upPos">
        update tbl_board set pos = pos + 1
    </update>
<!--  본문 읽기 끝 -->
 
<!-- 글쓰기 시작-->
    <insert id="insertBoard" parameterType="BoardDto">
        insert into tbl_board(seq,name,email,homepage,title,content,password,count,regdate,pos,depth)
        select case count(*) when 0 then 1 else max(seq) + 1 end,#{name},#{email},#{homepage},#{title},#{content},#{password},0,curdate(),0,0 from tbl_board    
    </insert>    
<!-- 글쓰기 종료 -->    
 
<!-- 글수정 시작 -->
    <update id="updateBoard" parameterType="map">
        update tbl_board set title=#{dto.title}, content=#{dto.content}, email=#{dto.email}, name=#{dto.name}
                whrere seq=#{dto.seq} and password=#{password} 
    </update>
<!-- 글수정 종료 -->
 
<!-- 글삭제와 넘어가는 화면 -->
    <select id="deleteView" parameterType="int" resultType="String">
        select password from tbl_board where seq=#{seq}
    </select>
    <delete id="deleteBoard" parameterType="map">
        delete from tbl_board where seq=#{seq} and password=#{storPass}
    </delete>
<!-- 글삭제 종료 -->
 
<!-- 답변달기와 넘어가는 화면 -->
    <insert id="replyboard" parameterType="BoardDto">
        insert into tbl_board(seq,name,email,homepage,title,content,password,count,regdate,pos,depth)
        select case count(*) when 0 then 1 else max(seq) + 1 end,#{name},#{email},#{homepage},#{title},#{content},#{password},0,curdate(),#{pos}+1,#{depth}+1 from tbl_board                
    </insert>
    <update id="replyUpPos" parameterType="BoardDto">
        update tbl_board set pos = pos + 1 where pos>#{pos}
    </update>
<!-- 답변달기 종료 -->
 
</mapper>


▲  ver.MySQL boardMapper소스코드


별반 다를게 없어 보이죠? 수정된 부분은 [36~39]번째 줄과 [59~62]번째 줄이 변경되엇습니다. autoincrement 로 시퀀스값을 올려주는 부분을 max로 변경하여 MySQL에서 사용가능하게 바꿔준 모습이죠. 자세한 설명은 아래 링크를 통해 확인하시기 바랍니다. 추가++))로 현재날짜는 오라클에서 sysdate지만 MYSQL에서는 curdate()를 사용합니다.






1
2
3
4
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/hunit?zeroDateTimeBehavior=convertToNull
username=root
password=1234



혹시 테스트하다가 오류내용에서 regdate쪽에서 오류가 생긴다면 db.properties에 들어가서 위 코드로 수정해주시기 바랍니다. url부분 끝에 ?zeroDateTimeBehavior = convertToNull의 코드를 넣어주면 초기 db에 날짜값을 안넣어줘도 오류로 안잡고 테스트가 가능하게 됩니다.

반응형