JSP & Servlet
71. 서비스 클래스 구현하기
서비스레이어 만들것이다. 서비스레이어에서 필요한 클래스가 notic클래스이고 필요한 함수들을 뽑아보았다.
NoticeService클래스를 만들고 함수를 만들기
이름이 같다는 것은 기능이 같다는 것임 개발자가 구현했다가는 코드 중복이 많아짐.
수정할때 어려움이 발생할 수 있다. 이름이 같으면? 세개중에 하나만 하고 나머지는 재호출기능으로 하자.
인자 제일 많은거 구현 자바 생성자 오버로딩 할때 배움. 6.7장 생성자 호출과 유사함.
public List<NoticeView> getNoticeList(){
return getNoticeList("title", "", 1);
}
public List<NoticeView> getNoticeList(int page){
return getNoticeList("title", "", page);
}
public List<NoticeView> getNoticeList(String field/*TITLE WRITER_ID*/, String query/*A*/, int page){}72. getNoticeList 메소드의 SQL 쿼리 작성하기
--getNoticeList 등록일을 기준으로 순서정렬 + 페이징이니 게시물 몇개씩만 보이게하기
SELECT * FROM NOTICE ORDER BY REGDATE DESC
우리가 등록하지 안했지만 자동으로 부여되는 로우넘버를 출력해서 사용하자 https://youtu.be/ekEBoJn2gF8하자
SELECT ROWNUM, NOTICE.* FROM NOTICE ORDER BY REGDATE DESC
이러면 로우넘버를 출력하고 정렬을하기때문에 섞인다.
https://youtu.be/PSf1bFJDBSI
서브쿼리를 이용해서 정렬을 먼저한후 하는 것임.
정렬된 결과물을 만든 후 이것을 기준으로 로우넘버 출력
SELECT ROWNUM, N.*
FROM (SELECT * FROM NOTICE ORDER BY REGDATE DESC) N;
결과물은 더이상 노티스가 아니니 별칭을 만들어주고 별칭의 로우넘버를 출력해야함.
SELECT * FROM (
SELECT ROWNUM NUM, N.*
FROM (SELECT * FROM NOTICE ORDER BY REGDATE DESC) N
)
WHERE NUM BETWEEN 1 AND 5;
원래 SQL문에 WHERE절을 그냥 붙여버리면 모든게 실행된 후 WHERE절을 실행하는 거라 6~10을 볼수없다.
그래서 위를 또 하나의 서브쿼리로 만들어서 NUM이라는 별칭을 준 새로운 로우넘버를 만들어내고 그 NUM으로 WHERE절을 실행하자.
또다른 로우넘버뽑는 함수
SELECT * FROM(
SELECT ROW_NUMBER() OVER (ORDER BY REGDATE DESC) NUM,
NOTICE.* FROM NOTICE
)
둘중 아무거나 사용해도 된다.
String sql = "SELECT * FROM (" +
"SELECT ROWNUM NUM, N.* " +
"FROM (SELECT * FROM NOTICE WHERE "+ field + " LIKE ? ORDER BY REGDATE DESC) N " +
") " +
"WHERE NUM BETWEEN ? AND ?";73 getNextNotice 메소드의 SQL 쿼리 작성하기
getNotice(int id)의 SQL은 다음과 같다.
String sql = "SELECT * FROM NOTICE WHERE ID=?";
그럼 Next와 Prev 다음과 이전이 필요하기 때문에 id가필요한 서브쿼리가 필요할듯하다.
게시물이 3번이고 12357 이있다면 3을 얻고 5와 2를 얻어오는 순서로 가야한다.
등록일자를 기준으로 역정렬되있는 상태임. 다음글을 얻으려면 큰놈을 골라야한다. 기준점이 regdate가 될것이다.
id를 얻고 redate가 큰놈
SELECT ID FROM NOTICE
WHERE REGDATE > (SELECT REGDATE FROM NOTICE WHERE ID = 3)
5번만 가져올것은? 순차정렬해버리고 로우넘버를 가져와서 첫번째 레코드를 가져오면된다.
SELECT ID FROM NOTICE
WHERE ID = (
SELECT ID FROM NOTICE
WHERE REGDATE > (SELECT REGDATE FROM NOTICE WHERE ID = 3)
AND ROWNUM = 1
)
String sql = "SELECT ID FROM NOTICE "
+ "WHERE ID = ( "
+ "SELECT ID FROM NOTICE "
+ "WHERE REGDATE > (SELECT REGDATE FROM NOTICE WHERE ID = ?) "
+ "AND ROWNUM = 1"
+ " )";74. getPrevNotice 메소드의 SQL 쿼리 작성하기
SELECT ID FROM NOTICE
WHERE ID = (
SELECT ID FROM (SELECT * FROM NOTICE ORDER BY REGDATE DESC)
WHERE REGDATE < (SELECT REGDATE FROM NOTICE WHERE ID = 3)
AND ROWNUM = 1
)
반대로 쓰면되는데 정렬되서 나오니 역정렬을 하기 위해 또 서브쿼리를 작성해서 역정렬된 NOTICE에서 ID를 뽑게 만들자
String sql = "SELECT ID FROM NOTICE "
+ "WHERE ID = ( "
+ " SELECT ID FROM (SELECT * FROM NOTICE ORDER BY REGDATE DESC) "
+ " WHERE REGDATE < (SELECT REGDATE FROM NOTICE WHERE ID = ?) "
+ " AND ROWNUM = 1 "
+ ")";75. getNoticeList의 JDBC 코드 구현하기
SQL문을 만들어놧으니 JDBC라이브러리를 가져오자.
컨트롤러의 JDBC 문들을 다 가져오고 컨트롤러에는 뷰단에 포워드하는 내용만 남겨놓는다.
컨트롤러 예제보기
15 610 111510 11~20 이런식으로 나와야하는데 이부분을 어떻게 할까?
sql문에 1
앞부분은 등차수열임 an = a + (n - 1)d 그러니 an = 1+(page-1)*10 1부터시작이기때문
뒷부분은 10 , 20 ,30 ,40 이니 page * 10
WHERE TITLE LIKE ? string filed가 title일때 String query %a%하면a가 포함되는 제목이 찾아진다.
https://gent.tistory.com/401 %%참조하기.
string filed도 ?로 해서 넣으면되지않느냐? 그런데 나중에 들어갈때 문자열이라 sql에 'title'이런식으로 들어가서 검색이 안된다.
+field+그래서 덧셈연산자로 넣어주자.
PreparedStatement st = con.prepareStatement(sql);
st.setString(1, "%" + query + "%");
st.setInt(2, 1+(page-1)*10);
st.setInt(3, page *10);76. NoticeService 클래스 완성하기
getNoticeCount() 게시물목록을 가져오는데 페이징이 되지않은 게시물이 몇갠지
https://gent.tistory.com/274참조
오라클의 count함수 사용함.
COUNT(*) - 조회된 전체행 건수를 반환한다
COUNT(컬럼)- 컬럼의 값이 NULL인 행은 카운트 하지 않는다
COUNT(DISTINCT 컬럼) - 컬럼 값을 중복제거하고, 컬럼의 값 건수를 반환한다
SELECT COUNT(ID) COUNT FROM
컬럼 ID의 개수를 세는것임. 개수를 얻어서 rs.getInt("count");해야하는데
인자가 COUNT(ID)를 넣을 수없으니 별칭으로 COUNT넣어줌 다시 가져다쓸때느 대소문자 구분안해도됨.
getNotice() id로 들어간 글 내용을 보여주는 것임. notice 객체를 만들어서 리턴하면됨.
next, prev 도 비슷함.
77. 목록 페이지에서 검색 기능 구현하기
검색과 페이징 구현해보자
검색 칸에 값을 넣고 검색
String field = "";
String query = "";
String field 에는 두가지 값을 받을건데
<select name="f">
<\option value="title">제목</option>
<option value="writerId">작성자</option>
옵션의 타이틀과 아이디 이것들이 f라는 키로 전달이 된다.
<input type="text" name="q" value=""/> 사용자가 입력한 값이 q로 입력이됨
list?f=title&q=a 이런식의 쿼리를 만들어서 서버에 요청을 할 것이다.
검색은 필수가 아니라 선택이기 때문에 field값에 null이 올수도잇어서 기본값을 만들어줘야한다.
검색후에 상태가 그대로 유지하도록 헤주자.
<option ${(param.f == "title")?"selcted":""} value="title">
<input type="text" name="q" value="${param.q}"/>
78. 목록에서 페이징 구현하기
페이지넘어가면 다음 목록 불러오고 검색시에도 검색한 목록의 다음 페이지 나오게 해주자
http://localhost:8080/notice/list?p=2&t=&q=
타이틀과 쿼리가 없어져서 상태값이 유지가 안됫다.
사실 검색하면 검색과 맞물려서 옵션, 검색어, 페이징이 같이가야하는 것이다.
String page_ = request.getParameter("q");
int page = 1;
if (page_ != null){
page = Integer.parseInt(page_);
}
List<Notice> list = service.getNoticeList(field, query, page);
페이지 값을 받아서 주면된다.
더 추가해야하는 내용?
1.데이터가 없는 페이지는 안가게하기
2.현재번호가 하이라이트되기
3.끝번호까지만 나오게하기
79. Pager에서 현재 페이지 번호 처리
데이터가 많이 나오기위해 데이터를 추가해주자
INSERT INTO NOTICE (ID, TITLE, WRITER_ID, CONTENT, REGDATE, HIT, FILES ) VALUES (SEQ_BNO.NEXTVAL, '제목'||SEQ_BNO.NEXTVAL, 'chun', 'aaa', SYSDATE, 0, null);
'제목'||SEQ_BNO.NEXTVAL 하면 문자열에 시퀸스를 붙일수있다.
${(empty param.p)?1:param.p}현재페이지 표시하기
null이거나 비엇으면 1 아니면 param.p
현재번호만 스타일주기
${(param.p == (startNum+i))?'orange':''}
그런데 이러면 p값이 없을때 기본값을 생각해봐야한다.
그래서${(page == (startNum+i))?'orange':''}해주면된다.
page는 위에서 사용했기때문에 이미 기본값으로 1이 들어가도록 되어있다.
80. Pager에서 마지막 번호 처리하기
마지막번호로 가면 더이상 못가야한다.
먼저 필요한것 마지막번호가 무엇이냐? 총 몇페이지 가있느냐
db에 레코드가 몇개가 있느냐에 따라 마지막번호를 정할 수있다.
그래서 컨트롤러에서 서비스로 레코드 개수가 몇개인지 받아와야한다.
100개가있다하면 10개씩 보여주고 있으니 100/10이다.
getNoticeCount(field, query)함수를 사용해서 레코드 수가 총 몇개인지 가져오는것임.
그런데 count/10 을 햇을때 나머지가 남으면? 문제가생긴다.
그래서 Math클래스의 ceil을 사용해보자.
10.2->11 Math.ceil(10.2) Math.floor 10.2->10
우리는 소수점없애고 위로 보낼것이니 실링하자.
정적메소드는 el에서 사용할 수 있다. 또한 11.0이 되기때문에 소수점을 잘라내는 함수 사용해야함.
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
을사용해서 잘라내자.
<c:set var="lastNum" value="${fn:substringBefore(Math.ceil(count/10), '.'}" />
el에서는 정적메소드를 그냥 사용할 수 있어서 가져다 사용한 모습이고 fn의 substringBefore은 지정한 구분자 앞에만 나타내는 함수이다.
(startNum+i) <= lastNum일때만 번호가 나오게해서 없으면 페이지 자체가 안나오게해버리기
81. 자세한 페이지 수정하기
detail은 서비스레이어를 나눈게아니라 컨트롤러자체가 jdbc자체를 받아왓다
이번엔 컨트롤러에서 서비스레이어를 사용해보자.
NoticeDetailController에서 getNotice(id)를 사용해보자
그런데 사용자는 전혀달라진게 없는데 굳이 나눠야하나?
사용자를 위한게 아니라 개발자를 위한 방식이다. 더 편한 부분으로 만드는게 낫다. 더 좋은 부분으로 작성하자.
여러사람이 나눈다고 생각햇을때 이것을 다른사람이 만든다면? 자기 분야만 만들면되니 훨씬 편하게 될것이다.
소규모에 컨트롤러 몇개없으면 그냥 합치는게 낫다.
다음강의에선 목록의 제목옆에 댓글 개수 표시해주기추가해보자
82. 목록에 댓글 수를 포함하려면?
여기까지는 가장 기본적인 스택이다.
제목53(3) 이런식으로 제목 옆에 댓글수를 달아주자.
먼저 디테일에 댓글을 해줘야한다.
하지만 이런 틀이 없으니 데이터베이스에다가만 추가해보자.
데이터베이스의 COMMENT 테이블에 댓글 추가하기
83. 목록에 댓글 수를 포함하기 위한 쿼리 문제
notice와 comment가 보여져야하니 두개의 테이블의 join이 필요하다
select * from notice N
INNER JOIN "COMMENT" C ON N.ID = C.NOTICE_ID
order by N.regdate desc;
INNER JOIN 은 자식이 테이블이 기준이되서 자식이 필요로하는 부모테이블의 컬럼내용을 가져온다.
그래서 left outer join사용하자 그냥 left만해도된다.
select * from notice N
left JOIN "COMMENT" C ON N.ID = C.NOTICE_ID
order by N.regdate desc;
그런데 자식때문에 부모가 반복되니까 noitce가 중점이 되서 나오길원한다.
자식은 집계할때만 사용하기때문에 모든게 나올필요가없다.
SELECT N.ID, N.TITLE, N.WRITER_ID, N.REGDATE, N.HIT, N.FILES, COUNT(C.ID) CMT_COUNT from notice N
LEFT JOIN "COMMENT" C ON N.ID = C.NOTICE_ID
GROUP BY N.ID, N.TITLE, N.WRITER_ID, N.REGDATE, N.HIT, N.FILES
order by N.regdate desc;
그루핑을 해보자. 부모테이블은 전부다나오게(컨텐츠는 용량이커서 다안나오고 잘려서빼기)
자식테이블은 count()를 사용해서 갯수만 나오게 하자.
이 SQL문을 지난 getNoticeList()에 집어 넣었던 SQL문과 합쳐야한다!
84. 목록의 댓글 수를 위한 View 생성하기
서브쿼리가 두번이나 사용됫는데 SQL문의 NOTICE에 조인문을 때려박으면 어지러워진다. 그래서 차선책을 사용해야한다.
그것은 바로 조인문을 결과집합으로 하는 NOTICE_VIEW를 만들어서 사용하자.
https://youtu.be/ONrIT4onN54 강의 참조하자.
CREATE VIEW NOTICE_VIEW
AS
SELECT N.ID, N.TITLE, N.WRITER_ID, N.REGDATE, N.HIT, N.FILES, COUNT(C.ID) CMT_COUNT from notice N
LEFT JOIN "COMMENT" C ON N.ID = C.NOTICE_ID
GROUP BY N.ID, N.TITLE, N.WRITER_ID, N.REGDATE, N.HIT, N.FILES;
--order by N.regdate desc;
SELECT * FROM (
SELECT ROWNUM NUM, N.*
FROM (SELECT * FROM NOTICE_VIEW WHERE "+ field + " LIKE '%%' ORDER BY REGDATE DESC) N
)
WHERE NUM BETWEEN 1 AND 10
order by는 기존 SQL문에서 하고있었으니 뺀다.
VIEW 는 테이블을 대신함으로써 데이터가 필터링된다거나 정렬된다고 할때 뷰에 포함안시키는 게 바람직하다
원본데이터를 추가하는 데 컬럼을 추가하는 것으로 만드는것이다.
뷰는 업무에 따라 여러 테이블들을 조인해서 만들어 놓는거임.
String sql = "SELECT * FROM (" +
"SELECT ROWNUM NUM, N.* " +
"FROM (SELECT * FROM NOTICE_VIEW WHERE "+ field + " LIKE ? ORDER BY REGDATE DESC) N " +
") " +
"WHERE NUM BETWEEN ? AND ?";
를 해서 보여주게할 수 있다.
그냥 Notice생성자를 추가하는게아니라 NoticeView라는 클래스를 만들고 Notice를 상속받게하자
cmtCount만 게터세터를 추가한 새로운 것을 만들면된다.
public class NoticeView extends Notice {
private int cmtCount;
public int getCmtCount() {
return cmtCount;
}
public void setCmtCount(int cmtCount) {
this.cmtCount = cmtCount;
}
public NoticeView() {
}
public NoticeView(int id, String title, Date regdate, String writerId, int hit, String files, int cmtCount) {
super(id, title, regdate, writerId, hit, files, "");
this.cmtCount = cmtCount;
}} String sql = "SELECT * FROM (" +
"SELECT ROWNUM NUM, N.* " +
"FROM (SELECT * FROM NOTICE_VIEW WHERE "+ field + " LIKE ? ORDER BY REGDATE DESC) N " +
") " +
"WHERE NUM BETWEEN ? AND ?";2023.01.04 리뷰
오늘의 오류
권한이 없어서 오라클에 view를 못만듬..!
https://gigle.tistory.com/84 해결함.
jsp도 끝이 보인다. 완벽하게는 아니지만 다시 배울때 당황하지는 않을 정도의 수준까지는 될 것같다.
중요한것은 꺾이지 않는 마음
'기초단계 > JSP&Servlet' 카테고리의 다른 글
| 2023.03.14 Servlet (0) | 2023.03.15 |
|---|---|
| 2023.01.06 JSP & Servlet (0) | 2023.01.07 |
| 2023.01.03 JSP & Servlet (1) | 2023.01.03 |
| 2023.01.02 JSP & Servlet (0) | 2023.01.02 |
| 2022.12.31 JSP & Servlet (0) | 2022.12.31 |