2023.04.12 JSP
newlec JSP
91. 일괄삭제 구현하기
삭제버튼 누르고 일괄삭제를 구현해보자.
목록을 받앗으니 서비스로직이 일을하면된다.
정상적으로 지우면 지워진상태에서 목록을 볼 수있는 겟요청을 하면된다.
url을 같이 쓰고잇지만 post요청에서 get요청을 호출하는 것이다.
클라이언트가 아니라 서버측에서 서버에 다른 url에서 요청하듯이 재요청을 하면된다.
response.sendRedirect("list");
아이디는 기본적으로 정수형이니 정수형으로 전달하는게 낫다.
여러 방법이 있지만 배열복사해서 int 배열로 바꿔주고 넣어주자.
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String[] openIds = request.getParameterValues("open-id");
String[] delIds = request.getParameterValues("del-id");
String cmd = request.getParameter("cmd");
switch (cmd) {
case "일괄공개":
break;
case "일괄삭제":
NoticeService service = new NoticeService();
int[] ids = new int[delIds.length];
for (int i = 0; i < delIds.length; i++) {
ids[i] = Integer.parseInt(delIds[i]);
}
int result = service.deleteNoticeAll(ids);
break;
}
response.sendRedirect("list");
}91.2 함수구현
함수는 jdbc를 이용해야한다.
id가 여러개 들어가야하니 WHERE ID IN()을 넣어주자.
그런데 in()에 ,로 구분된 배열이 들어가야하는데 JDBC에서 배열이 들어가는게 있느냐? 없다.
그래서 값을 꽃아넣기 위해 배열을 넣을 수있도록 반복문으로 배열을 만들어줘야한다.
i가 마지막보다 작을 경우를 제외하고 ,를 넣어서 ,가 포함된 문자열을 만들어주자.
public int deleteNoticeAll(int[] ids) {
int result = 0;
String params = "";
for (int i = 0; i < ids.length; i++) {
params += ids[i];
if (i < ids.length - 1) {
params += ",";
}
}
try {
Class.forName("com.mysql.cj.jdbc.Driver");
Connection con = DriverManager.getConnection(dbURL, dbID, dbPwd);
String sql = "DELETE FROM NOTICE WHERE ID IN (" + params + ")";
PreparedStatement pstmt = con.prepareStatement(sql);
result = pstmt.executeUpdate();
pstmt.close();
con.close();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}92. 공지사항 등록을 위한 Controller/View 준비하기
먼저 regController생성해준다.
여기서 해야하는 일은 1.겟요청 2.포스트요청 두가지 일을 해야한다.
글쓰기버튼을 누르면 글쓰기 위한 페이지 제공(get) -> 등록시 포스트(post)
String title; String content; String isOpen;
3가지 상태를 보내야한다.
@WebServlet("/admin/board/notice/reg")
public class RegController extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
RequestDispatcher dis = request.getRequestDispatcher("/WEB-INF/view/admin/board/notice/reg.jsp");
dis.forward(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String title = request.getParameter("title");
String content = request.getParameter("content");
String isOpen = request.getParameter("open");
}
}문제점이 있는데
1.open이 null or true라 직접 db에 가기 힘들다.
2.한글이깨진다. 필터이용해야한다.
93. pub 컬럼 추가에 대한 변경사항 처리
한글을 받으면서 깨졌엇는데 이번에는 response의 문제엿다.
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html; charset=UTF-8");문제는 사용자가 전달한 open을 0또는 1로 받아야한다.
열하나 추가했을 뿐인데 수정사항이 정말 많다
db테이블에만 pub가잇고 응용프로그램에는 준비가 안되있다.
그런데 말한사람은 쉽지만 고칠사람은 정말 많은 것을 고쳐야하기 때문이다.
받을 수있는 담는 그릇의 속성도 바꿔줘야한다.
1.notice 변경
notice에 필드를 추가하고 생성자와 getter/setter를 설정해줘야한다.
자동으로 만들면 boolean의 게터는 isXX이름이니 get이름으로 바꿔서 쉽게 사용할수 있도록 하자.
2.데이터베이스 수정
NOITCE_VIEW PUB추가하기
3.NOTICEVIEW 변경하기
4.NoticeService에서 데이터를 담는 쿼리들이 모두 오류가 발생한다.
정말 할게 많다 수정하는게 쉽지않다.
하나 고치는것에서 매우 할게 많다. 빠르게 고치는 것도 능력이긴하다.
컨트롤러 이름이 겹쳐서 url을 계속 고쳐줘야한다.
/admin/board/notice/list을 웹루트에 만들어주면 걔를 우선시해서 나오게 된다.
꼼수로 파일을 선택하면 이 url로 열리는데 매핑된게 대신 나오게 된다.
편하게 보기 위해서 이런식으로 사용해보자.
파일명으로 요청을 하는데 매핑된게 우선적으로 나오게 되는 꼼수이다.
94. 공지사항 등록하기
public int insertNotice(Notice notice){}
서비스를 먼저 작성해야한다.
public int insertNotice(Notice notice) {
int result = 0;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
Connection con = DriverManager.getConnection(dbURL, dbID, dbPwd);
String sql = "INSERT INTO NOTICE(TITLE, CONTENT, WRITER_ID, PUB) VALUES(?,?,?,?)";
PreparedStatement pstmt = con.prepareStatement(sql);
pstmt.setString(1, notice.getTitle());
pstmt.setString(2, notice.getContent());
pstmt.setString(3, notice.getWriterId());
pstmt.setBoolean(4, notice.getPub());
result = pstmt.executeUpdate();
pstmt.close();
con.close();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}제목, 내용, 작성자, pub를 값으로 넣어주면된다.
작성자는 원래는 관리자를 해서 인증과 권한을 설정해야하지만 일단 그냥 넣어주자.
그리고 작성후 list페이지를 다시 보여준다.
경로를 지정하지 않으면 현재 가지고 있는 url의 것을 요청하게 된다.
가지고 있는 경로의 listcontroller를 부르게된다.
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html; charset=UTF-8");
String title = request.getParameter("title");
String content = request.getParameter("content");
String isOpen = request.getParameter("open");
boolean pub = false;
if(isOpen != null) {
pub = true;
}
Notice notice = new Notice();
notice.setTitle(title);
notice.setContent(content);
notice.setPub(pub);
notice.setWriterId("newlec");
NoticeService service = new NoticeService();
service.insertNotice(notice);
response.sendRedirect("list");
}95. 관리자 공지사항 자세한 페이지 추가
사용자가 보는 디테일 페이지와 크게 다르지 않다.
일반페이지와 다른점은 수정과 삭제 버튼이있다.
첨부파일은 fortoken으로 나눳다 그걸 넣자.
<tr>
<th>제목</th>
<td class="text-align-left text-indent text-strong text-orange" colspan="3">
${notice.title}
</td>
</tr>
<tr>
<th>작성일</th>
<td class="text-align-left text-indent" colspan="3">${notice.regdate }</td>
</tr>
<tr>
<th>작성자</th>
<td>${notice.writerId}</td>
<th>조회수</th>
<fmt:formatNumber value= "${notice.hit}"/>
</tr>
<tr>
<th>첨부파일</th>
<td colspan="3" style="text-align: left">
<c:forTokens var="fileName" items="${notice.files}" delims="," varStatus="st">
<a href="${fileName}">${fn:toUpperCase(fileName)}</a>
<c:if test="${!st.last}">
/
</c:if>
</c:forTokens>
</td>
</tr>
<tr class="content">
<td colspan="4">${notice.content}</td>
</tr>96. 파일업로드를 위한 인코드 multipart/form-data
https://youtu.be/Ao8ZiZR_DqU
파일업로드를 위해서는 파일을 어떻게 인코딩하는지 멀티파트인코디이란 뭘의미하는지
등록페이지를 통해 등록데이터는 어떻게 등록이 되는지를 알아야한다.
reg.jsp의 폼태그
<form method="post" action="reg" enctype="application/x-www-form-urlencoded">
하면 인코딩타입을 정해줄수있다.
기본인코딩타입은 url을 사용할 수 있는 형태로 인코딩이 된다는 것이다.
그러나 이렇게하면 파일명만 전송된다.
인코딩 타입을 multipart/form-data로 바꿔줘야한다.
url인코딩방식으로 가면 문자열만가고
multipart 는 문자열, 문자열, 바이너리 데이터가 가게된다.
이 데이터를 서버쪽에서 받아서 사용해야한다.
인코딩방식을 변경하면 제목이 전달이 안되서 null값이 들어가 전달이 안되게 된다.
인코딩방식을 바꾼것에서 설정을 해줘야한다.
97. 파일 업로드를 위한 서블릿 설정
클라이언트설정을 변경하였으니 서버쪽에서도 multipart/form-data으로 데이터를 받아서 인코딩해야한다.
설정은 web.xml을 설정하거나 어노테이션으로 설정할 수 있다.
xml로 설정을 하면 필요하면 그냥 가져다 쓰면된다. 어노테이션은 필요한 클래스마다 작성해줘야한다.
어노테이션은 클래스에 적어준다.(type)
<multipart-config>
<location>/tmp</loaction>
<max-file-size>20848820</max-file-size>
<max-request-size>418018841
<file-size-threshold>1048576
</multipart-config>@MultipartConfig(
location="/tmp",
fileSizeThreshold=1024*1024,
maxFileSize=1024*1024*5,
maxRequestSize=1024*1024*5*5)location fileSizeThreshold 이 두속성은 연결되어있다.
용량이 클경우 메모리에 저장하면 서버 쪽 메모리가 넘어간다.
일정메모리가 넘어가면 디스크를 사용해서 임시저장하도록 해야한다.1메가바이트(1024*1024)가 넘으면 디스크를 사용하자.
maxFileSize, maxRequestSize은 파일을 보낼때 첨부파일을 여러개 보낼수도있다. 파일두개가 전송된다.
전체네트워크를 통해 받는 데이터가 굉장히 큰사이즈가 된다.
너무많이 받으면 디도스처럼 서비스가 마비되는 수가있다.
이것을 제한을 거는 것이다.
maxFileSize은 하나의 파일 사이즈의 맥시멈사이즈를 정하는 것이다.
맥시멈 1024(1kb)*1024(->1mb)*5 = 5메가바이트
이걸 여러개 보낼수잇으니 maxRequestSize 전체요청에 대한 파일크기를 정할 수 있다.
1024*1024*5*5은 5메가*5메가-> 25메가 로한다는 것이다.
location fileSizeThreshold은 속성에 대한 내용이 애매하다.
로케이션은 절대경로를 사용해야한다. -> 그런데 기본값이 있다.
상대경로를 쓸수없고 절대경로를 구해야하는데 config하는동안 코딩이되는 것이아니기때문에 미리 설정하는 것이 불가능하다.
그래서 설정을 하지 않는다. 차라리 설정을 하지 말고 자바가 지정한 곳을 사용하게 되는 것이다.
이렇게 멀티파트로 인코딩방식을 바꾸면 그것을 서버쪽에서도 멀티파트로 인코딩할수있도록 설정을 해줘야한다.
@MultipartConfig(
fileSizeThreshold=1024*1024,
maxFileSize=1024*1024*50,
maxRequestSize=1024*1024*50*5)
@WebServlet("/admin/board/notice/reg")
public class RegController extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
RequestDispatcher dis = request.getRequestDispatcher("/WEB-INF/view/admin/board/notice/reg.jsp");
dis.forward(request, response);
}98. 파일저장을 위한 물리경로 얻기
파일을 포스트하기 위해서 인코딩 하고 서버가 받는 설정을 했다.
실제 파일을 받아서 저장하기 위해서는 경로를 얻어야한다.
클라이언트가 보낸 내용을 서버쪽에서 받고 저장하기 위해 저장경로를 어떻게 할 것인가를 설정해야한다.
<td colspan="3" class="text-align-left text-indent">
<input type="file" name="file" />
</td>이름은 파일이고 형식은 파일로 담아져있다.
우리가 필요한것은 키값(name)인 'file'이다.
이 part값 정보의 컨텐츠를 얻어야한다.
request의 도구중 getPart / getParts()메소드로 Part객체를 생성할 수 있다.
전자는 전달한 매개 값을 가지고 특정파트를 얻는것이다.
후자는 파트전체를 받아오는것이다.
Part filePart = request.getPart("file");
이 파트로 부터 인풋스트림을 만들어서 경로에 저장을 해야한다.
filePart.getInputStream();
/upload라는 디렉토리에 저장하려면 어떻게 해야할까?
request.getServletContext();를 사용하면 웹루트를 통해서 루트를 통한 상대경로를 넘겨주면
getRealPath()메소드로 시스템의 실제물리경로를 얻어올 수 있다.
upload라는 파일을 만들고 하면 왜안되나?
보이는 루트는 개발을 하기 위한 임시 위치일뿐이다.
이클립스 workspace에 .metadata안에 저장된다.
개발하는 개발루트에 저장되는 것이 아니라 서비스되고있는 어플리케이션에 저장이된다.
99. 단일 파일 업로드
사용자가 전달한 파일을 실제 물리경로에 저장하는 작업을 해보자
출력은 리얼패스에 저장에 해야한다.
실제 풀경로는 realPath + File.separator + fileName에 파일명을 얹어야한다.
fileName은 파일파트에서 getSubmittedFileName();로 얻을 수 있다.
중간에 구분자도 넣어줘야하는데 가운데 "//"이런식으로 넣으면안되고 자바가 사용하고 잇는 경로 구분방법인 File.separator을 사용해야한다.
File.separator가 그 구분자이다.
이제 아웃풋스트림으로 파일을 출력하면된다.
Part filePart = request.getPart("file");
InputStream fis = filePart.getInputStream();
String fileName = filePart.getSubmittedFileName();
String realPath = request.getServletContext().getRealPath("/upload");
String filePath = realPath + File.separator + fileName;
FileOutputStream fos = new FileOutputStream(filePath);
byte[] data = new byte[1024];
while (true) {
int size = fis.read(data);
if (size == -1) {
break;
}
fos.write(data, 0 , size);
}
fos.flush();
fos.close();
fis.close();100. 다중 파일 업로드
파일이 여러개 여도 차이가 크지는 않다.
두개를 만들어줘서 전체 목록화해서 받아주자.
<th>첨부파일</th>
<td colspan="3" class="text-align-left text-indent">
<input type="file" name="file" />
<input type="file" name="file" />
</td>몇개가 올지 모르니 파트를 컬렉션으로 받아줘야한다.
반복문을 통해서 값을꺼내고 파일이면 Part filePart에 저장해주면된다.
파일업로드를 했으니 db에 파일명을 저장해야한다.
StringBuilder builder = new StringBuilder();
builder.append(fileName);
builder.append(",");
파일면 , 파일명, 해주기
마지막엔 ,가들어가면안되니까 마지막은 ,을 빼주면된다.
builder.delete(builder.length()-1, builder.length());
notice.setFiles(builder.toString());로 담아주면된다.
Collection<Part> parts = request.getParts();
StringBuilder builder = new StringBuilder();
for(Part p: parts) {
if (!p.getName().equals("file")) {
continue;
}
Part filePart = p;
InputStream fis = filePart.getInputStream();
String fileName = filePart.getSubmittedFileName();
String realPath = request.getServletContext().getRealPath("/upload");
String filePath = realPath + File.separator + fileName;
builder.append(fileName);
builder.append(",");
FileOutputStream fos = new FileOutputStream(filePath);
byte[] data = new byte[1024];
while (true) {
int size = fis.read(data);
if (size == -1) {
break;
}
fos.write(data, 0, size);
}
fos.flush();
fos.close();
fis.close();
}
builder.delete(builder.length()-1, builder.length());101. 업로드 된 파일 사용하기
업로드된 파일을 사용해야한다.
파일을 누르면 다운이 되지않고 404에러가뜬다.
detail의 첨부파일 영역을 수정해줘야한다.
하이퍼링크가 경로가빠져있고 이름만 추가가되어있다.
그러면 현재 url을 기반으로해서 파일명이 추가가되는 것이다.
"/upload/${fileName}" upload에 있다는 것을 나타내주자.
그런데 이미지파일이 열리긴하는데 다운로드가 안된다. 옛날에는 다운로드가 어려웠으나
a download href= "/upload/${fileName}" html 코드로 download속성만 추가해주면된다.
<tr>
<th>첨부파일</th>
<td colspan="3" style="text-align: left">
<c:forTokens var="fileName" items="${notice.files}" delims="," varStatus="st">
<a download href="/upload/${fileName}" >${fn:toUpperCase(fileName)}</a>
<c:if test="${!st.last}">
/
</c:if>
</c:forTokens>
</td>
</tr>첨부파일로만 사용하는게 아니라 첨부한 파일을 태그화해서 게시판의 내용에 넣고 싶다면
content에 파일넣는방법
<img src="/upload/img1.png"> 이런식으로 html을 넣으면된다.
그러면 태그를 모르면 어떻게쓰나? 보통은 웹 편집기가 있어서 클릭으로 될 수 있도록 하는 메뉴가 존재한다.
102. 파일 업로드 마무리
다중파일 업로드가 되도록 했는데 오류가 발생할 수있다.
1.파일을하나만 넣으면 오류발생한다.
for문을 돌리면서 넣는데 파일이 두개가 전달된다. 멀티파트로 가면서 파일을 선택하지 않아도 전달되다가 오류가 발생한다.
if(p.getSize() == 0) {
continue;
}사이즈가 0인경우 파일이긴한데 실제 데이터로서 바이너리를 전달하지 않고 그냥 비어있는 데이터가 포스트가 될때 처리하는 것이다.
이렇게 하면 파일을 하나만 전달할 수있다.
2.개발하면서 upload파일을 만들었다.
실제서비스에서 이것을 배포한다면 만드는게 필요가없다. 그래서 삭제를 할것이다. 이걸 삭제하면 경로가없어서 에러가 난다.
실제 경로가 없다면 패스가 없다면 만들어주는 작업을 해줘야한다.
File객체에서 실제 물리적인 경로를 얻어냇을때 물리적인 경로가 있느냐라는 것을 알아낼수잇다.
File path = new File(realPath);
if(!path.exists()) {
path.mkdirs();
}경로가 없을 경우 경로를 만들어주자.
책임져야할것이 두가지가있다.
1.예외가 발생하면 사용자화면에도 오류가 뜬다. 그래서 예외처리를 통해 원하는 메시지로 해줘야한다.
2.파일을 저장할때 파일명이 기존의 파일명과 같을 수도 있다. 동일한 파일명이 잇을경우 어떻게 저장해줄까를 지정해줘야한다.
일련번호를 부착하던지 다른 것으로 해보자. 스스로 고민하기!
103. 공개 설정 이용하기
파일업로드 공지사항 등록을 했다.
공개 설정하기.
checkbox를 checked하면 체크가 된다.
기본값은 빈상태로 두고 pub가 true라면 체크상태로 바꿔주면된다..
<c:set var="open" value=""/>
<c:if test="${n.pub == true}">
<c:set var="open" value="checked"/>
</c:if>
<input type="checkbox" name="open-id" ${open} value="${n.id}">그런데 사용자페이지에서도 공개된것만 보여야한다.
jsp파일에서 설정할수도있지만 조건처리를 여기서하면 레코드가 10개가 왔는데 다 비공개면 하나도 못보여줄수도 있다.
조건처리가 10번을 돌긴했지만 비어있는 공지성분을 보게된다.
여기서는 무조건 10개를 보고 컨트롤러에서 SQL문을 처리해줘야한다.
public List\<NoticeView> getNoticeViewPubList(String field, String query, int page)
NoticeService에 공개된리스트 메소드를 만들어주자.
String sql = "SELECT * FROM NOTICE_VIEW WHERE PUB = 1 AND " + field + " LIKE ? ORDER BY REGDATE DESC LIMIT ?, ?";을 해줘서 1인것만 검색되게하자.
104. 공지사항 일괄 공개하기
관리자가 체크 한경우 배열이 보여지는 것이다.
체크 안된거 사라졋으니 전달해야한다. 체크가 id를 이용해서 전달되면1 아니면0로 해줘야한다.
전달된것을 어떻게 하는가 체크가 안된것을 어딘가에 숨겨놓는거이다.
페이지의 공지사항에 id가 어떤것들이엇다를 숨겨놓자.
다음에 일괄공개를 누를때 이것을 전달하고 숨겨져있는 목록에 있는 모든 공지사항에 id들을 한번에 전달하는것이다.
원래목록에 있던것들을 비교해서 체크하는 것이다.
서밋버튼위에 하나를 심어주자. 위에서는 사용자에게 보여질 목록을 위해 사용되엇다면 여기서는 그 id들만 추출해서 보여준다.
<c:set var="ids" value=""/>
<c:forEach items="${notice}" var="notice">
<c:set var="ids" value="${ids} ${notice.id}"/>
</c:forEach>
<input type="hidden" name="ids" value="${ids}"/>
List<String> oids = Arrays.asList(openIds);
for (int i = 0; i < ids.length; i++) {
if(oids.contains(ids[i])) {
pub -> 1
}else {
pub -> 0;
}
service.pubNoitceList(openIds); //update notice set pub = 1 where id in(..);
service.closeNoticeList(clsIds); //update notice set pub = 0 where id in(..);
break;105. 비공개할 id 목록 얻기
공개해야할 id를 얻는것도 중요하지만 이것을 기반으로 비공개할 id를 얻는 것도 중요하다.
지난시간에 for문으로 도는것도 좋지만 귀찮다.
전체목록 1,2,3,4,5,6,7,8,9,10 - 공개목록 3,5,8을 빼주면 비공개할 id목록이 얻어진다.
아이디들을 배열로만들고 가변길이로 사용하기 위해 새 List에 담아준다.
그리고 List에서 공개 아이디들을 지우면된다.
List<String> oids = Arrays.asList(openIds);
List<String> cids = new ArrayList(Arrays.asList(ids));
Arrays.asList(ids).removeAll(oids);106. 공개를 위한 서비스 함수의 수정사항
service.pubNoitceList(openIds);
service.closeNoticeList(clsIds);
만약 서비스함수를 두개만들어서 공개비공개처리를햇을때
하나만 실행되고 하나는 예외가되어서 실행안되면 오류가 발생한다.
하고싶은 단위를 하다가 실패하게 된다.
이 하고싶은 단위를 '트랜잭션' 이라고한다.
Transaction 즉 한번에 잘 실행되어야한다.
실패하면 앞으로 돌려줘야한다. 업무단위를 완전히 하나로 하게 되어야한다.
서비스함수를 하나처럼 되도록 해야한다.
설계단계에서 하나의 인자로 만들었엇지만 트랜젝션을 위해 이것을 수정해주자.
public int pubNoitceAll(int[] oids, int[] cids) {
return 0;
}그러나 넘기고자한게 배열이아니라 컬렉션이다.
그럼 맞춰서 다시 배열로 바꿀건가 사용자(서버)를 위해서 다양하게 전달할수 있게 할것인지를 정해야한다.
제공자가 무조건하나만 제공하도록 하거나 서버가 다른 형식으로 다변화해서 다양하게 사용하게 할 수도 있다.
public int pubNoitceAll(int[] oids, int[] cids) {
return 0;
}
public int pubNoitceAll(List<String> oids, List<String> cids) {
return 0;
}
public int pubNoitceAll(String oidsCSV, String cidsCV) {
return 0;
}다양하게 넣을수있도록 인트배열, 컬렉션, 아예문자열인 CSV로받앗을때를 처리할수잇게해주자.
이렇게 오버로드를 통해서 제공자측에선 여러개 제공하고 사용자가 원하는 것을 사용할 수있게 해주는게 좋다.
제공자에선 오버로드해서 제공하고 사용자에선 자기가 원하는 것중 흡사한것을 선택적으로 구현하면된다.
SQL구현할때 업데이트 문 안에 ?,?,?,?로 넣는거보다 CSV로 구분된 ?하나로 오는게 더좋을 수있다.
그래서 CSV를 구현해서 재호출시키는 것으로 하는것도 좋다.
107. 오버로드 서비스 메소드 구현하기
1.int배열을 CSV로
관건은 int 배열을 CSV로 바꾸는 것이다.
String의 join함수를 사용하자.
String.join("구분자", 문자, 문자, ...) 구분자로 알아서 값을 넣을 수 있게 해준다.
구분자로 가변인자를 계속 넣으면 구분자를 가지고 하나로 만들어준다.
이것은 가변데이터를 나열해야되서 그냥 배열을 넣는게 안되서 배열을 문자열 리스트로 바꾸고 넣어줘야한다.
public int pubNoitceAll(int[] oids, int[] cids) {
List<String> oidsList = new ArrayList<>();
for (int i = 0; i < oids.length; i++) {
oidsList.add(String.valueOf(oids[i]));
}
List<String> cidsList = new ArrayList<>();
for (int i = 0; i < cids.length; i++) {
oidsList.add(String.valueOf(cids[i]));
}
String oidsCSV = String.join(",", oidsList);
String cidsCSV = String.join(",", cidsList);
return pubNoitceAll(oidsCSV, cidsCSV);
}List를 변수로 받는 메소드는 위의 방법을 사용하면되는데 처음부터 리스트니 그냥 사용하면된다.
public int pubNoitceAll(List<String> oids, List<String> cids) {
String oidsCSV = String.join(",", oids);
String cidsCSV = String.join(",", cids);
return pubNoitceAll(oidsCSV, cidsCSV);
}이제 마지막을 구현해주면되는데 더 할말이 있다 그것은 다음 강의에서 보자.
여기서 리스트를 stream으로 할 수 있지만 오히려 복잡해지거나 느려질수 있으니
단순하게 for문으로 끝나는 것은 for문으로 해주는게 좋다.
108. pubNoticeAll 구현하기
문자열형태를 그대로 반영하거나 조건처리해서 바꿀수도잇다.
둘다 실행되어야해서 SQL문을 두개 만들어주자.
String sqlOpen = "UPDATE NOTICE SET PUB=1 WHERE ID IN (?)";
String sqlClose = "UPDATE NOTICE SET PUB=0 WHERE ID IN (?)";문자열에 문자열이 꽃혀지면 에러가 난다. 분리해서 문자열 더하기로 넣어줘야한다.
String sqlOpen = "UPDATE NOTICE SET PUB=1 WHERE ID IN (" + oidsCSV + ")";
String sqlClose = "UPDATE NOTICE SET PUB=0 WHERE ID IN (" + cidsCSV + " )";중간에 덧셈연자가 들어가면 복잡하게 보이니 String의 format을 이용해서 할수도잇다.
public int pubNoitceAll(String oidsCSV, String cidsCSV) {
int result = 0;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
Connection con = DriverManager.getConnection(dbURL, dbID, dbPwd);
String sqlOpen = String.format("UPDATE NOTICE SET PUB=1 WHERE ID IN (%s)", oidsCSV);
PreparedStatement pstmt = con.prepareStatement(sqlOpen);
result += pstmt.executeUpdate();
String sqlClose = "UPDATE NOTICE SET PUB=0 WHERE ID IN (" + cidsCSV + " )";
pstmt = con.prepareStatement(sqlClose);
result += pstmt.executeUpdate();
pstmt.close();
con.close();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}2023.04.12
트랜잭션->페이지집중화->인증->DAO의 내용이 더 잇지만 더이상 올라오지 않는다.
이런 부분들은 스프링이 쉽게 설정할 수 있도록 도와준다.
JSP는 뷰를 위주로 보고 컨트롤러에서 쓰인 알고리즘들을 스프링에가서 연계하면될듯하다.