영보의 SystemOut.log

[JSP,MyBatis] CRUD 게시판 만들기(1) #업로드/다운로드/수정/삭제 본문

Web/JSP

[JSP,MyBatis] CRUD 게시판 만들기(1) #업로드/다운로드/수정/삭제

영보로그 2020. 9. 24. 22:45
반응형

앞서 한것에 이어서 오늘은 JSP와 Java, MyBatis를 이용하여

첨부파일 업로드, 다운로드, 수정, 삭제가 가능한 게시판을 만들어 보겠습니다.

 

 

 

먼저 사용한 라이브러리입니다.

또한 한글이 깨지지 않게, UTF-8로 환경설정을 해줬습니다.

 

 

# Oracle에 테이블 생성

1
2
3
4
5
6
7
8
9
10
11
12
CREATE TABLE databoard4(
   no NUMBER,
   name VARCHAR2(34CONSTRAINT db4_name_nn NOT NULL,
   subject VARCHAR2(1000CONSTRAINT db4_sub_nn NOT NULL,
   content CLOB CONSTRAINT db4_cont_nn NOT NULL,
    pwd VARCHAR2(10CONSTRAINT db4_pwd_nn NOT NULL,
   regdate DATE DEFAULT SYSDATE,
   hit NUMBER DEFAULT 0,
   filename VARCHAR2(260),
   filesize NUMBER DEFAULT 0,
   CONSTRAINT db4_no_pk PRIMARY KEY(no)
);
cs
1
2
INSERT INTO databoard7(no,name,subject,content,pwd,filename)
VALUES(1,'홍길동','마이바티스 curd이용법','JSP include가 있는 경우 처리방법','1234',' ');COMMIT;
cs

 

 

# db.properties

1
2
3
4
driver=oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@211.238.142.195:1521:XE
username=hr
password=happy
cs

- 일반 파일을 추가해서 경로/IP/Oracle id/pw 

 

# DataBoardVO.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
package com.sist.dao;
import java.util.*;
public class DataBoardVO {
    private int no;
    private String name;
    private String subject;
    private String content;
    private String pwd;
    private Date regdate;
    private int hit;
    private String filename;
    private int filesize;
    private String dbday;
    public int getNo() {
        return no;
    }
    public void setNo(int no) {
        this.no = no;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getSubject() {
        return subject;
    }
    public void setSubject(String subject) {
        this.subject = subject;
    }
    public String getContent() {
        return content;
    }
    public void setContent(String content) {
        this.content = content;
    }
    public String getPwd() {
        return pwd;
    }
    public void setPwd(String pwd) {
        this.pwd = pwd;
    }
    public Date getRegdate() {
        return regdate;
    }
    public void setRegdate(Date regdate) {
        this.regdate = regdate;
    }
    public int getHit() {
        return hit;
    }
    public void setHit(int hit) {
        this.hit = hit;
    }
    public String getFilename() {
        return filename;
    }
    public void setFilename(String filename) {
        this.filename = filename;
    }
    public int getFilesize() {
        return filesize;
    }
    public void setFilesize(int filesize) {
        this.filesize = filesize;
    }
    public String getDbday() {
        return dbday;
    }
    public void setDbday(String dbday) {
        this.dbday = dbday;
    }
}
cs

- 미리 Oracle에 생성해놓은 데이터를 VO에서 이클립스와 연동하기 위해 getter/setter로 캡슐화 코딩을 해줍니다.

 

 

 

# DataBoardDAO.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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
package com.sist.dao;
// XML파싱후에 => 실행된 결과를 받는 위치=>DAO에서 메소드 호출 
import java.io.*;//XML파일 읽기 => Reader (한글파일)
import java.util.*;//List => 구현 (ArrayList);
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class DataBoardDAO {
   // XML을 파싱(데이터 읽기) => 읽은 데이터 저장 ==> SqlSesionFactory
   private static SqlSessionFactory ssf;
   // 초기화 => XML을 읽기(자동으로 셋팅)
   static 
   {
       try
       {
           // 1. XML파일 읽기
           Reader reader=Resources.getResourceAsReader("Config.xml");
           // Config에 등록되어 있는 모든 XML파일 읽는다 
           // 파일명에 대소문자를 구분한다
           // 2. XML을 파싱 : 필요한 데이터 읽어 간다 (마이바티스 라이브러리가 읽어 간다) => 저장을 하고 사용한다
           ssf=new SqlSessionFactoryBuilder().build(reader);
           // 파싱 => SAX(읽기전용) => 태그를 한개씩 읽어와서 필요데이터를 저장하는 방식
       }catch(Exception ex)
       {
           // 에러처리 
           System.out.println(ex.getMessage());
       }   
   }
   public static List<DataBoardVO> boardListData(Map map)
   {
       List<DataBoardVO> list=new ArrayList<DataBoardVO>();
       // Connection을 사용한 다음에 반드시 반환 ==> SqlSession
       SqlSession session=null;
       // SqlSession => Connection과 동일한 역할 수행 
       // 컴파일 예외처리는 없다 => 초반에 에러처리하기 위해서 => 예외처리를 하는 것이 좋다 => null
       try
       {
           // 실행 : 정상수행 : try수행 => finally수행
           //      비정상  : 중간에 catch수행 => finally수행
           // 1. 미리 만든 Connection객체를 얻어 온다 
           session=ssf.openSession();
           // 2. XML에 있는 SQL문장을 실행후에 결과값을 달라
           list=session.selectList("boardListData",map);
       }catch(Exception ex)
       {
           ex.printStackTrace();//실행하는 과정을 보여준다 
       }
       finally
       {
           // 반환 => Connection=>close()
           if(session!=null)
               session.close(); 
           /*
            *   close()
            *   {
            *     if(ps!=null) ps.close();
            *     if(conn!=null) conn.close(); ==> disConnection()
            *   }
            */
       }
       return list;
   }
   // 총페이지 ==> id="boardTotalPage"
   // <select id="boardTotalPage" resultType="int">
   public static int boardTotalPage()
   {
       // 지역변수는 반드시 초기화 
       /*
        *    클래스 => null
        *    String => null
        */
       int total=0;
       SqlSession session=null;
       try
       {
           // 1. connection 객체를 가지고 온다 
           session=ssf.openSession();
           total=session.selectOne("boardTotalPage");
       }catch(Exception ex)
       {
            ex.printStackTrace();
       }
       finally
       {
           if(session!=null)//연결이 되어 있다면 
               session.close(); // 연결을 종료 (자동으로 반환 => 반환 다시 사용이 가능)
       }
       return total;
   }
   // 데이터 추가 
   public static void boardInsert(DataBoardVO vo)
   {
       SqlSession session=null;
       try
       {
           session=ssf.openSession(true);// commit을 실행한다 
           // INSERT,UPDATE,DELETE => 데이터베이스 변경 => 반드시 COMMIT
           session.insert("boardInsert",vo);
           // 여러개의 SQL문장을 실행하면 
           //session.commit();
       }catch(Exception ex)
       {
           // 에러 처리 
           ex.printStackTrace();
       }
       finally
       {
           if(session!=null// 오라클에 연결되어 있다면 
               session.close();
       }
   }
   public static DataBoardVO boardDetailData(int no)
   {
       // 데이터를 받을 변수 설정 
       DataBoardVO vo=new DataBoardVO();
       // 오라클 연결 
       SqlSession session=null;// Connection => SqlSession안에 Connection기능을 가지고 있다 
       // 데이터를 가지고 오는 과정 에러가 발생할 수 있다 => 사전에 에러방지 프로그램 => 예외처리 
       /*
        *    예외처리 
        *     = 직접 처리 (에러가 발생하면 바로 처리해서 사용) try~catch
        *     = 간접처리 (시스템 에러발생여부만 확인하고 에러 회피) throws 
        */
       try
       {
           // 정상적으로 실행 => 에러 => catch를 수행하고 점프 
           // 연결 
           session=ssf.openSession(); // Commit을 포함하지 않는다 
           // 작업 수행 
           // 1. 조회수 증가 
           // <update id="hitIncrement" parameterType="int">
           session.update("hitIncrement",no);
           session.commit();// 포함이 안되면 조회수가 증가를 하지 않는다 
           // 2. 증가된 조회수를 포함해서 데이터 가지고 오기
           // <select id="boardDetailData" resultType="DataBoardVO" parameterType="int">
           vo=session.selectOne("boardDetailData", no);
       }catch(Exception ex)
       {
           // 에러가 났을 경우에 처리 (복구) 
           ex.printStackTrace();//어떤 에러가 났는지 확인 
       }
       finally
       {
           // 정상수행 ,비정상 수행 상관없이 => 무조건 수행 (서버연결 해제,데이터베이스 연결 해제)
           if(session!=null// 연결되어 있다면
               session.close();  // ps.close(), conn.close() => disConnection()
       }
       
       return vo;// 사용자가 요청한 데이터를 받아 볼 수 있다
   }
   
   // 찾기 관련 내용 
   // 찾은 갯수 
   // <select id="boardFindCount" resultType="int" parameterType="hashmap">
   public static int boardFindCount(Map map)
   {
       int count=0;
       // 연결(오라클) => 객체
       SqlSession session=null;
       try
       {
           session=ssf.openSession();
           count=session.selectOne("boardFindCount",map);
       }catch(Exception ex)
       {
           // 에러 처리 
           ex.printStackTrace();
       }
       finally
       {
           if(session!=null)
               session.close(); // conn.close() => DBCP (반환=>재사용)
       }
       return count;
   }
   public static List<DataBoardVO> boardFindData(Map map)
   {
       List<DataBoardVO> list=new ArrayList<DataBoardVO>();
       // 연결 객체
       SqlSession session=null;
       try
       {
           // 객체주소 얻기
           session=ssf.openSession(); // COMMIT(X)
           list=session.selectList("boardFindData",map);
       }catch(Exception ex)
       {
           ex.printStackTrace();
       }
       finally
       {
           if(session!=null)
               session.close();
       }
       return list;
   }
   // 삭제 하기 
   // JSP(요청) ==> databoard-mapper.xml ==> DAO 처리 ==> JSP에서 받아서 화면 출력
   public static boolean boardDelete(int no,String pwd)
   {
       boolean bCheck=false;
       SqlSession session=null;
       try
       {
           // Conection
           session=ssf.openSession();
           // 비밀번호 가지고 오기
           // <select id="boardGetPassword" resultType="String" parameterType="int">
           String db_pwd=session.selectOne("boardGetPassword",no);
           
           if(pwd.equals(db_pwd))
           {
               bCheck=true;
               // 실제 삭제
               // <delete id="boardDelete" parameterType="int">
               session.delete("boardDelete",no);
               // 반드시 commit
               session.commit();
           }
           else
           {
               bCheck=false;
           }
           
       }catch(Exception ex)
       {
           ex.printStackTrace();
       }
       finally
       {
           // session을 닫는다 (connection,preparedstatement 포함)
           if(session!=null)
               session.close();
       }
       return bCheck;
   }
   // <select id="boardGetInfoData" resultType="DataBoardVO" parameterType="int">
   public static DataBoardVO boardGetInfoData(int no)
   {
       DataBoardVO vo=new DataBoardVO();
       SqlSession session=null;
       try
       {
           // 연결 
           session=ssf.openSession();
           vo=session.selectOne("boardGetInfoData",no);
       }catch(Exception ex)
       {
           // 에러 처리
           ex.printStackTrace();
       }
       finally
       {
           // 닫기
           if(session!=null)
               session.close();
       }
       return vo;
   }
   public static DataBoardVO boardUpdateData(int no)
   {
       DataBoardVO vo=new DataBoardVO();
       SqlSession session=null;
       try
       {
           session=ssf.openSession();
           vo=session.selectOne("boardDetailData",no);
       }catch(Exception ex)
       {
           ex.printStackTrace();
       }
       finally
       {
           if(session!=null)
               session.close(); 
       }
       return vo;
   }
   // 수정하기
   public static boolean boardUpdate(DataBoardVO vo) {
       boolean bCheck=false;
       SqlSession session=null;
       try {
           session=ssf.openSession();
           String db_pwd=session.selectOne("", vo.getNo());
           if(db_pwd.equals(vo.getPwd())) { 
               bCheck=true;
               // 본인이면
               session.update("boardUpdate",vo);
               // 저장한다
               session.commit();
           }
           else { //본인이 아니면
               bCheck=false;
           }
       }catch(Exception ex) {
           // 에러처리
           ex.printStackTrace();
       }
       finally {
           if(session!=null)
               session.close(); // 연결종료 => 다시 사용하기 위한 반환
       }
       return bCheck;
   }
}
cs

- DAO : xml 파싱 후 결과값을 받는 위치로 DAO에서 메소드를 호출해준다.

- Config.xml에 등록되어 있는 모든 XML파일을 읽는다.

- XML을 파싱하면 마이바티스 라이브러리가 필요한 데이터를 읽어간다. => 저장 후 사용

- SAX(읽기전용)

: 태그를 한개씩 읽어와서 필요한 데이터를 저장하는 방식

JAXP
   SAX : 읽기 => 일반적으로 모든 라이브러리는 SAX방식을 사용한다 => MyBatis,Spring...
JAXB
   binding

 

 

 

#databoard-mapper.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
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
<?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.sist.dao.databoard-mapper">
  <select id="boardListData" resultType="DataBoardVO" parameterType="hashmap">
     SELECT no,subject,name,TO_CHAR(regdate,'YYYY-MM-DD') as dbday,hit,num 
     FROM (SELECT no,subject,name,regdate,hit,rownum as num 
     FROM (SELECT no,subject,name,regdate,hit 
     FROM databoard1 ORDER BY no DESC))
     WHERE num BETWEEN #{start} AND #{end}
  </select>
  <select id="boardTotalPage" resultType="int">
    SELECT CEIL(COUNT(*)/10.0) FROM databoard1
  </select>
  <!-- 내용보기 : 다운로드 -->
  <!-- 중복된 문장이 있는 경우 처리:재사용 -->
  <sql id="where-no">
    WHERE no=#{no}
  </sql>
  <!-- 조회수 증가 -->
  <update id="hitIncrement" parameterType="int">
    UPDATE databoard1 SET
    hit=hit+1
    <include refid="where-no"/>
  </update>
  <!-- 실제 데이터 읽기 -->
  <select id="boardDetailData" resultType="DataBoardVO" parameterType="int">
    SELECT no,name,subject,content,TO_CHAR(regdate,'YYYY-MM-DD') as dbday,
           hit,filename,filesize 
    FROM databoard1
    <include refid="where-no"/>
  </select>
  <!-- 추가  -->
  <!-- DataBoardVO  #{name} vo.getName() , #{pwd} vo.getPwd()-->
  <!--  Map에 데이터를 묶어서 전송 , VO에 데이터를 묶어서 전송  
        VO에 소속된 변수 => VO
        VO에 없는 변수가 있는 경우 => Map
  -->
  <!-- <insert id="boardInsert" parameterType="DataBoardVO">
    INSERT INTO databoard VALUES(
      (SELECT NVL(MAX(no)+1,1) FROM databoard),#{name},#{subject},
      #{content},#{pwd},SYSDATE,0,#{filename},#{filesize}
    )
  </insert> -->
  <insert id="boardInsert" parameterType="DataBoardVO">
    <!-- Sequence 
         SELECT NVL(MAX(no)+1,1) FROM databoard을 먼저 수행한 후에
                 결과값을 no에 받아서 
         INSERT 문장에 추가
    -->
    <selectKey keyProperty="no" resultType="int" order="BEFORE">
      SELECT NVL(MAX(no)+1,1) FROM databoard1
    </selectKey>
    INSERT INTO databoard1 VALUES(
      #{no},
      #{name},
      #{subject},
      #{content},
      #{pwd},
      SYSDATE,
      0,
      #{filename},
      #{filesize}
    )
  <!-- 수정하기 -->
  <update id="boardUpdate" parameterType="DataBoardVO">
      UPDATE databoard1 SET
      name=#{name},
      subject=#{subject},
      content=#{content}
      <include refid="where-no"/>
  </update>
  <!-- 삭제하기 -->
  <select id="boardGetPassword" resultType="String" parameterType="int">
      SELECT pwd FROM databoard1
      <include refid="where-no"/>
  </select>
  <!-- 게시물이 삭제가 되면 업로드된 파일을 삭제 -->
  <select id="boardGetInfoData" resultType="DataBoardVO" parameterType="int">
      SELECT filename, filesize FROM databoard1
      <include refid="where-no"/>
  </select>
  <!-- 본인일 경우에는 게시물을 삭제 -->
  <delete id="boardDelete" parameterType="int">
      DELETE FROM databoard1
      <include refid="where-no"/>
  </delete>
  <!-- 찾기 -->
  <!-- 
          ${fd} => 컬럼명,테이블명  (name LIKE , subject LIKE, content LIKE)
          #{ss} => 일반 데이터 
   -->
  <select id="boardFindData" resultType="DataBoardVO" parameterType="hashmap">
    SELECT no,subject,name,TO_CHAR(regdate,'YYYY-MM-DD') as dbday,hit
    FROM databoard1 
    WHERE ${fd} LIKE '%'||#{ss}||'%'
  </select>
  <select id="boardFindCount" resultType="int" parameterType="hashmap">
    SELECT COUNT(*)
    FROM databoard1
    WHERE ${fd} LIKE '%'||#{ss}||'%'
  </select>
</mapper>
cs

- mapper : SQL문장이 어디에 있는지 확인하기 위해서 사용

- XML을 씀으로써 귀찮은 코드들이 줄고 사용자 정의기 때문에 데이터 전달이 더 쉬워집니다

  추가로 Connection, PreParedStatement, ResultSet 등등을 MyBatis에서 해주기 때문에 유지보수에도 용이합니다.

 

 

# Config.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
<?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>
  <properties resource="db.properties"/>
  <typeAliases>
    <!-- VO를 등록 -->
    <typeAlias type="com.sist.dao.DataBoardVO" alias="DataBoardVO"/>
  </typeAliases>
  <!-- 오라클 연결하는 부분 : getConnection() -->
  <environments default="development"><!-- 개발 환경을 만든다 -->
    <environment id="development">
       <transactionManager type="JDBC"/>
      <!-- 오라클 정보를 모아서 MyBatis 라이브러리에 전송 : DataSource -->
       <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>
  <mappers>
    <mapper resource="com/sist/dao/databoard-mapper.xml"/><!-- 분산 여러개를 등록이 가능 -->
  </mappers>
</configuration>
cs

 * UNPOOLED : 요청(SQL문장 실행)할때 오라클 연결 , 결과값을 가지고 오면 오라클 연결 해제
   - 단점 : 연결하는 시간이 많이 소모(연결이 지연될 수 도 있다)
 * POOLED : DBCP (미리 Connection을 연결하고 요청시마다 연결된 Connection을 넘겨주고 사용 후 반환

   - 재사용이 가능
   - 연결하는 소모되지 않는다 
   - Connection의 생성 갯수를 제어 할 수 있다
   - 일반적으로 웹프로그램에서는 기본으로 사용

 

 

포스팅 양이 많아져서 

두번째 포스팅으로 뵙겠습니다.

 

반응형