이제까지 실습을 spring4버전으로 했는데, spring5버전에 맞는 자바로만 하는 개발방법을 배웠다.
나머지는 다 xml을 이용한 방식과 똑같은데 한가지 다른 점은 config폴더에 들어가는 xml 파일들에 해당하는 내용이 전부 자바로 이루어져 있다는 점이다.
먼저 jsp화면을 찾아 띄워주는 viewResolver 는 아래와 같이 자바로 구현할 수 있다.
@Bean
public ViewResolver viewResolver()
{
InternalResourceViewResolver r=new InternalResourceViewResolver();
r.setPrefix("/");
r.setSuffix(".jsp");
return r;
}
InternalResourceViewResolver라는 객체를 생성해서 셋팅이 가능하다.
그리고 클래스에서 WebMvcConfigurer를 상속받으면 dispatcherServlet에 연결할 수 있는 메소드를 오버라이딩 할 수 있다.
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
datasource에 해당하는 메소드
@Bean
public DataSource dataSource()
{
BasicDataSource ds=new BasicDataSource();
ds.setDriverClassName("oracle.jdbc.driver.OracleDriver");
ds.setUrl("jdbc:oracle:thin:@localhost:1521:XE");
ds.setUsername("hr");
ds.setPassword("happy");
ds.setMaxActive(10);
ds.setMaxIdle(10);
ds.setMaxWait(-1);
return ds;
}
sql문장을 찾아주는 sqlsessionfactory 메소드
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception
{
SqlSessionFactoryBean ssf=new SqlSessionFactoryBean();
ssf.setDataSource(dataSource());
Resource res=new ClassPathResource("Config.xml");
ssf.setConfigLocation(res);
return ssf.getObject();
}
그리고 이후에 동작하는 것은 전과 같다.
그 다음에 연습해본건 자료를 업로드 하는 객체를 활용하는 방법
자료 여러개를 올리기 위해서 필요한 클래스를 application-context에 먼저 등록해준다.
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver"
/>
라이브러리에서 CommomsMultipartResolver라는 객체를 불러온다. 컨테이너에 등록해두면 여러개의 파일을 업로드하는 기능을 사용할 수 있다.
그리고 mapper에서 기능을 처리해줄 sql문장을 써준다.
<!-- 등록 -->
<insert id="databoardInsert" parameterType="com.sist.dao.DataBoardVO">
<!-- Sequence처리 -->
<selectKey keyProperty="no" resultType="int" order="BEFORE">
SELECT NVL(MAX(no)+1,1) as no FROM spring_databoard
</selectKey>
INSERT INTO spring_databoard VALUES(
#{no},
#{name},
#{subject},
#{content},
#{pwd},
SYSDATE,
0,
#{filename},
#{filesize},
#{filecount}
)
</insert>
다른 부분은 일반 게시판과 같기 때문에 생략
글을 등록해줄때 어떻게 처리해주고 있는지 보겠다. 먼저 글을 등록할때 글 번호를 자동적으로 1씩 증가시켜주기 위해
시퀀스 처리를 아래와 같이 해준다.
<!-- Sequence처리 -->
<selectKey keyProperty="no" resultType="int" order="BEFORE">
SELECT NVL(MAX(no)+1,1) as no FROM spring_databoard
</selectKey>
no 값을 1 증가시켜 주는것이기 때문에 keyProperty는 no, resultType은 당연히 int, 명령은 insert를 실행하기 전에 먼저 실행하라고 BEFORE을 써준다.
그 이후에는 insert문을 작성해준다.
INSERT INTO spring_databoard VALUES(
#{no},
#{name},
#{subject},
#{content},
#{pwd},
SYSDATE,
0,
#{filename},
#{filesize},
#{filecount}
)
</insert>
파일이름, 사이즈, 파일이 몇개인지 세주는 count변수를 저장해준다.
상세보기에서는 역시 조회수를 1 증가시켜준 다음 글 내용을 가져와 주기 위해서 sql문을 써준다.
<!-- 상세보기 -->
<update id="databoardHitIncrement" parameterType="int">
UPDATE spring_databoard SET
hit=hit+1
WHERE no=#{no}
</update>
<select id="databoardDetailData" resultType="com.sist.dao.DataBoardVO" parameterType="int">
SELECT * FROM spring_databoard
WHERE no=#{no}
</select>
그리고 삭제를 위한 sql문장까지 작성해주면 완성
<!-- 비밀번호 확인 -->
<select id="databoardGetPassword" resultType="string" parameterType="int">
SELECT pwd FROM spring_databoard
WHERE no=#{no}
</select>
<!-- 파일명 읽기 -->
<select id="databoardFileInfoData" resultType="com.sist.dao.DataBoardVO" parameterType="int">
SELECT filename, filecount FROM spring_databoard
WHERE no=#{no}
</select>
<!-- 삭제 -->
<delete id="databoardDelete" parameterType="int">
DELETE FROM spring_databoard
WHERE no=#{no}
</delete>
확실히 myBatis를 사용하니 sql문장을 쓰는것은 한결 간결해졌다.
DAO에서 처리해주는 부분
// 상세보기
// databoardHitIncrement
public DataBoardVO databoardDetailData(int no)
{
// 조회수 증가
getSqlSession().update("databoardHitIncrement",no);
// 실제데이터 읽기
return getSqlSession().selectOne("databoardDetailData", no);
}
상세보기할때 처리해주는 부분. 조회수를 증가시키고 글번호를 주고 실제 데이터를 insert해준다.
// 삭제
public DataBoardVO databoardFileInfoData(int no)
{
return getSqlSession().selectOne("databoardFileInfoData", no);
}
public boolean databoardDelete(int no, String pwd)
{
boolean bCheck=false;
String db_pwd=getSqlSession().selectOne("databoardGetPassword", no);
if(db_pwd.equals(pwd))
{
bCheck=true;
getSqlSession().delete("databoardDelete", no);
}
return bCheck;
}
글을 삭제할때는 저장되어 있는 파일도 같이 삭제되어야지 서버 컴퓨터의 용량을 아낄 수 있기 때문에 삭제하기전에 삭제할 글에서 파일 정보를 받아오는 메소드를 작성해준다.
그리고 삭제할때는 사용자가 입력한 비밀번호와 테이블에 저장되어 있는 비밀번호를 비교해서 맞으면 삭제 메소드를 실행할 수 있도록 하는 메소드를 만든다.
그리고 필요한 작업을 dispatcher에 요청하기 위해서 controller파일을 만든다.
그 전에 먼저,
글을 써서 파일을 테이블에 저장하는 과정이다. 파일은 여러개를 처리해주기 위해 파일명을 배열에 담아뒀다.
<script type="text/javascript">
let fileIndex=0;
$(function(){
$('#addBtn').click(function(){
$('#user-table').append(
'<tr id="m'+fileIndex+'">'
+'<td>파일 '+(fileIndex+1)+':<input type=file name=files['+fileIndex+'] size=20>'
+'</td></tr>'
)
fileIndex++;
});
$('#delBtn').click(function(){
if(fileIndex!=0)
{
$('#m'+(fileIndex-1)).remove();
fileIndex--;
}
})
});
</script>
파일 인덱스를 0값으로 초기화 해두고, files[] 배열에 인덱스를 0부터 저장해준다.
VO에서도 파일들을 처리할 수 있는 List배열을 만들어뒀다.
private List<MultipartFile> files; // 파일 업로드 했을때 저장되는 공간 => 여러개가 넘어온다
/*
* <input type=file name="files[0]">
* <input type=file name="files[1]">
* <input type=file name="files[2]">
*/
else // 업로드가 된 상태
{
String tempFile="";
String tempSize="";
for(MultipartFile mf:list)
{
try
{
String strFile=mf.getOriginalFilename();
File file=new File("c:\\spring-upload\\"+strFile);
mf.transferTo(file);// 서버에 업로드(서버 폴더에 파일 저장)
// 오라클에 파일명을 묶어서 저장 a.jpg,b.png,c.jpg
tempFile+=strFile+",";
tempSize+=file.length()+",";
}catch(Exception ex){}
}
tempFile=tempFile.substring(0,tempFile.lastIndexOf(","));
tempSize=tempSize.substring(0,tempSize.lastIndexOf(","));
vo.setFilename(tempFile);
vo.setFilesize(tempSize);
vo.setFilecount(list.size());
}
dao.databoardInsert(vo);
return "redirect:list.do";
파일이 업로드가 되었을때 위와 같이 처리를 해준다. ,를 전부 붙여서 저장해줬기 때문에 오라클 테이블에 넣기 전에는 마지막 ,를 제거하고 넣어준다. 그리고 다시 list.do로 넘어갈 수 있게 처리를 한다.
다음은 상세보기
// 상세보기 : 출력 화면 => String
@GetMapping("databoard/detail.do")
public String databoard_detail(int no,Model model)
{
DataBoardVO vo=dao.databoardDetailData(no);
List<String> fn=new ArrayList<String>();
List<String> fs=new ArrayList<String>();
String s1=vo.getFilename();
String s2=vo.getFilesize();
if(vo.getFilecount()!=0)
{
StringTokenizer st=new StringTokenizer(s1,",");
while(st.hasMoreTokens())
{
fn.add(st.nextToken());
}
st=new StringTokenizer(s2,",");
while(st.hasMoreTokens())
{
fs.add(st.nextToken());
}
model.addAttribute("fn", fn);
model.addAttribute("fs", fs);
}
model.addAttribute("vo", vo);
return "databoard/detail";
}
오라클에서는 여러개의 파일명과 파일사이즈를 한 문자열로 저장했기 때문에, 자바에서는 다시 List에서 나눠서 저장을 해준다. 그래서 StringTokenizer를 사용해줬다.
<c:if test="${vo.filecount!=0 }">
<tr>
<th width=20% class="danger text-center">첨부파일</th>
<td colspan="3">
<ul style="list-style-type: none">
<c:forEach var="f" items="${fn }" varStatus="s">
<li><a href="download.do?fn=${f }">${f }</a>(${fs[s.index]}Bytes)</li>
</c:forEach>
</ul>
</td>
</tr>
</c:if>
그러면 위와 같이 첨부파일 여러개를 상세보기에서 띄울 수 있게 된다.
서버 컴퓨터에 이렇게 저장이 됐다.
그리고 이걸 다운로드 받을 수 있게 만들어주는 controller
// 다운로드 : void
@GetMapping("databoard/download.do")
public void databoard_download(String fn,HttpServletResponse response)
{
try
{
File file=new File("c:\\spring-upload\\"+fn);
// 파일 정보 얻기
response.setHeader("Content-Disposition", "attachement;filename="
+URLEncoder.encode(fn, "UTF-8"));
response.setContentLength((int)file.length());
BufferedInputStream bis=new BufferedInputStream(new FileInputStream(file));
// 서버에서 파일을 읽어 온다
BufferedOutputStream bos=new BufferedOutputStream(response.getOutputStream());
// 다운로드하는 사람에게 파일을 보내준다
byte[] buffer=new byte[1024];
int i=0;// 읽은 바이트
while((i=bis.read(buffer, 0, 1024))!=-1) //-1 file end => EOF
{
// 다운로드하는 사람에게 보내라
bos.write(buffer, 0, i);
}
bis.close();
bos.close();
}catch(Exception ex){}
}
다운로드가 이렇게 완료된다.
삭제
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<style type="text/css">
.row{
width:800px;
margin: 0px auto;
}
h1 {
text-align: center;
}
</style>
</head>
<body>
<div style="height: 50px"></div>
<div class="container">
<div class="row">
<form method=post action="delete_ok.do">
<table vlass="table">
<tr>
<td>비밀번호:<input type=password name=pwd size=10 class="input-sm"></td>
<input type=hidden name=no value="${no }">
</tr>
<tr>
<td class="text-center">
<input type=submit value="삭제" class="btn btn-sm btn-danger">
<input type=button value="취소" class="btn btn-sm btn-danger"
onclick="javascript:history.back()">
</td>
</tr>
</table>
</form>
</div>
</div>
</
</body>
</html>
jsp는 이렇게 되어 있다.
delete_ok.do는 따로 컨트롤러를 빼서 만들었는데, ajax를 사용해서 사용할때는 model을 이용하지 않고, 바로 delete.jsp에 스크립트를 보내서 처리를 해주기도 한다
@RequestMapping(value="databoard/delete_ok.do")
public String databoard_delete_ok(int no, String pwd)
{
String msg="";
// 데이터 베이스에 저장되어 있는 파일 받기
DataBoardVO vo=dao.databoardFileInfoData(no);
boolean bCheck=dao.databoardDelete(no, pwd);
if(bCheck==true)
{
// 파일 지우기 => list.do이동
msg="<script>location.href=\"list.do\";</script>";
try
{
if(vo.getFilecount()!=0) // 파일이 있는 경우에만 삭제
{
StringTokenizer st=new StringTokenizer(vo.getFilename(), ",");
while(st.hasMoreTokens())
{
File file=new File("c:\\spring-upload\\"+st.nextToken());
file.delete();
}
}
}catch(Exception ex){}
}
else
{
// 스크립트 => 비밀번호가 틀립니다 메세지
msg="<script>"
+"alert(\"Password Fail!!\");"
+"history.back();"
+"</script>";
}
return msg;
}
비밀번호가 맞을 경우 파일이 있으면 지워주고, 아니면 비밀번호가 틀렸다는 메세지를 주고 뒤로 갈 수 있게 한다.
그리고 msg를 리턴해서 바로 delete.jsp에 띄워준다.
'programming > Gukbi' 카테고리의 다른 글
국비 교육 81일차 - annotation service (0) | 2021.04.21 |
---|---|
국비 교육 81일차 - xml, json 파싱 (0) | 2021.04.21 |
국비 교육 79일차 - spring project (cookie사용) (0) | 2021.04.19 |
국비 교육 78일차 - spring mvc 게시판 만들기, mvc 프로젝트 셋팅 (0) | 2021.04.18 |
국비 교육 77일차 - spring Autowired, web 연결 (0) | 2021.04.15 |