- 사원명을 클릭하면 "empView.do" 로 요청한다, 요청하면서 사원 번호인 empno 를 GET 방식으로 전달함
Controller 클래스 EmpController.java 에서 "empView.do" 요청 부분만
// 사원 상세페이지
@RequestMapping("empView.do")
public String empView(int empno, Model model) {
Emp emp = es.select(empno); // 사원 상세 정보 구하기
model.addAttribute("emp", emp);
return "emp/empView";
}
- 앞에서 넘어온 값인 사원번호를 @RequestParam (생략) 으로 바로 변수 empno 로 값을 받는다
- 자동으로 int 형으로 형변환되어 저장됨
- 1명에 대한 상세정보를 DB에서 구해와서 View 에 뿌려야 하므로 Model 객체를 매개변수에 선언해서 받음
<돌아온 후>
- 사원의 상세정보는 select() 가 리턴되면서 Emp DTO 객체에 저장되어 돌아온다, 그걸 Emp DTO 객체 emp 에 저장함
- 그 객체 emp 를 Model 객체에 저장하고 /WEB-INF/views/emp/empView.jsp 파일로 이동
- View 페이지로 이동할때 prefix, suffix 를 빼고 작성하고, 하위의 패키지는 적어줘야하므로 "emp/empView" 로 작성
Service 클래스 EmpServiceImpl.java 에서 select() 메소드 부분만
public Emp select(int empno) {
return ed.select(empno);
}
- DB 연동을 해야하므로 Service 로 넘어왔다
DAO 클래스 EmpDaoImpl.java 에서 select() 메소드 부분만
public Emp select(int empno) {
return sst.selectOne("empns.select", empno);
}
- root-context.xml 에서 SqlSession bean 객체를 생성했고, 그걸 DAO 클래스에 주입했으므로 selectOne() 사용 가능
- 전달된 사원번호 empno 를 selectOne() 을 호추랗며 그대로 전달함
- 사원 1명에 대한 상세정보를 구해야하므로 selectOne() 을 사용
Mapper 파일 Emp.xml 에서 id 가 "select" 인 태그 부분만
<select id="select" parameterType="int" resultType="emp">
select * from emp where empno=#{empno}
</select>
- 전달받은 사원 번호로 해당 사원의 상세정보(모든 정보) 를 검색함
- 전달받은 값은 사원번호이므로 전달받은 자료형 parameterType 은 "int"
- 돌려주는 자료형 resultType 은 Emp DTO alias 인 "emp"
- 다시 DAO -> Service -> Controller 로 갔다가 View 로 오게 된다
- emp.deptno (해당 사원의 부서) 와 dept.deptno (forEach에 의해 모든 부서의 부서번호가 차례대로 들어감) 가 같으면 selected 함으로서 해당 사원이 가입할때 등록했던 부서는 select-option 에서 선택되어 나타나도록 했다
+ jQuery 로 처리하면 간단하게 처리 가능
- 사원 번호을 readonly 속성으로 비활성화했다, readonly 이므로 값이 넘어간다!
- select-option 에 출력되는 내용은 부서명(부서번호) 형식이지만, 저장하는 값인 value 는 부서 번호가 된다
- 사원 수정폼에 수정할 정보를 입력하고 '수정' 을 누르면 action 인 "empUpdate.do" 로 요청한다
프로젝트 myBatis2 : 사원 정보 수정
Controller 클래스 EmpController.java 에서 "empUpdate.do" 요청 부분만
// 사원 정보 수정
@RequestMapping("empUpdate.do")
public String empUpdate(Emp emp, Model model) {
int result = es.update(emp);
model.addAttribute("deptno", emp.getDeptno());
model.addAttribute("result", result);
return "emp/empUpdate";
}
- 사원 수정폼에서 넘어온 값들을 @ModelAttribuate (생략) 을 사용해서 Setter 메소드들로 한번에 Emp DTO 객체인 emp 에 저장한다
- Emp Service 객체 es 를 사용해서 update() 메소드를 호출하고, 호출할때 수정할 정보를 저장한 객체 emp 를 전달
- 수정할 사원을 특정하는 사원 번호는 앞의 사원 수정폼에서 넘어왔으므로 emp 객체 안 emp.empno 에 저장되어있음
<돌아온 후>
- 수정 성공시 result 에는 1이 저장된다
- 수정이 끝난 후 부서 상세 페이지로 돌아가므로, 해당 사원이 소속된 부서의 부서번호가 필요하다, 그래서 emp.getDeptno() 를 "deptno" 네임으로 Model 객체에 저장
- View 페이지에서 수정 성공 / 실패 처리를 하기 위해 update() 에서 돌려받은 값 result 를 Model 객체에 저장
- /WEB-INF/views/emp/empUpdate.jsp 로 이동
Service 클래스 EmpServiceImpl.java 에서 update() 메소드 부분만
public int update(Emp emp) {
return ed.update(emp);
}
DAO 클래스 EmpDaoImpl.java 에서 update() 메소드 부분만
public int update(Emp emp) {
return sst.update("empns.update", emp);
}
- 수정할 정보를 담은 Emp DTO 객체 emp 를 받아서 그대로 전달
- 수정 성공시 자동으로 1을 리턴한다
Mapper 파일 Emp.xml 에서 id 가 "update" 태그 부분만
<update id="update" parameterType="emp">
update emp set ename=#{ename},job=#{job},sal=${sal},
comm=#{comm},deptno=#{deptno} where empno=#{empno}
</update>
- "emp" 는 MyBatis 환경설정 파일 Configuration.xml 에서 설정한 Emp DTO 의 alias 이다
- 객체 emp 가 넘어왔으므로 #{ename} 은 emp.getEname() 과 같은 의미
- 사원번호는 객체 emp 의 emp.empno 에 저장되어 있으므로 #{empno} 로 가져와서 where 절에 넣는다
- '삭제' 를 누르면 "empDelete.do" 료 요청한다, 요청하면서 사원 번호인 empno 를 전달
Controller 클래스 EmpController.java 에서 "empDelete.do" 요청 부분만
// 사원 삭제
@RequestMapping("empDelete.do")
public String empDelete(int empno, Model model) {
Emp emp = es.select(empno); // 사원 상세 정보 구하기
int result = es.delete(empno);
model.addAttribute("result", result);
model.addAttribute("deptno", emp.getDeptno());
return "emp/empDelete";
}
- 앞에서 넘어온 사원번호 empno 를 @RequestParam("empno") (생략) 을 통해 바로 매개변수의 empno 에 저장한다 사원 삭제할때 DB 연동 작업 2가지
1. 사원 상세 정보 구하기
- 사원을 삭제 한 후 그 사원이 속해있었던 부서의 부서 상세 페이지로 이동할 것이다, 그러려면 부서 번호가 필요함
- 부서 상세 페이지로 가기 위해, 삭제될 사원의 상세 정보를 구해서 거기서 부서 번호를 가져올 것 - 사원의 상세 정보를 구하기 위해 select() 메소드를 호출하며, 사원번호를 전달
2. 사원 삭제 하기
- 사원 삭제를 위해 delete() 메소드를 호출하며, 사원번호를 전달
<돌아온 후>
- 삭제된 사원 상세정보를 받은 emp 에서 emp.getDeptno() 로 사원이 소속되어있던 부서번호를 가져와서 Model 객체에 저장하고 empDelete.jsp 로 이동
1. 사원 상세 정보 구하기
- 이전에 여러번 했으므로 설명 생략
2. 사원 삭제하기
Service 클래스 EmpServiceImpl.java 에서 delete() 메소드 부분만
public int delete(int empno) {
return ed.delete(empno);
}
DAO 클래스 EmpDaoImpl.java 에서 delete() 메소드 부분만
public int delete(int empno) {
return sst.delete("empns.delete", empno);
}
Mapper 파일 Emp.xml 에서 id 가 "delete" 인 태그 부분만
<delete id="delete" parameterType="int">
delete from emp where empno=#{empno}
</delete>
- 삭제 성공시 "empList.do" 로 요청하면서 삭제된 사원의 소속 부서인 deptno 를 넘겨주면서 해당 부서 상세 페이지로 이동
- 50번 부서에 등록되어있는 사원 LAY2 를 삭제해보자
STS 에 Data Source Management 추가
- STS 에는 이클립스와 다르게 Data Source Management 가 없으므로 DB 연결 불가능하다
- STS 에 plug-in 기능을 추가해야 Data Source Management 를 사용 가능
- 설치 이후 이제 필요한 SQL문을 STS 내에서 실행할 수 있다
STS 에 Data Source Management 추가하는 방법
- 이러면 STS 에 plug-in 을 설치한다, 원격 저장소에서 로컬 저장소로 다운받음
- 그 후 STS 를 Restart 시키면 plug-in 이 추가되어있다
- 이후 이제 필요한 SQL문을 STS 내에서 실행할 수 있다
게시판 직접 만들어보기
설계도
spring 실습 설계도
프로젝트 명 spring
계정생성 sqlplus system/oracle create user spring identified by spring123;
권한 부여 (connect, resource 롤) grant connect, resource to spring;
테이블명 myboard
create table myboard( no number primary key, writer varchar2(20), passwd varchar2(20), subject varchar2(50), content varchar2(100), readcount number, register date );
시퀀스명 myboard_seq
create sequence myboard_seq;
구조 - base-package 는 myspirng 으로 한다 - prefix 하위에 board 가 있다 환경 설정 파일 작성(세팅) 순서 1) pom.xml 2) web.xml 3) servlet-context.xml 4) configuration.xml 5) board.xml 6) root-context.xml - 4), 5), 6) 은 DB 연동 관련 내용
오라클 계정 생성 및 권한 부여
- 기존 계정들은 복잡하므로 새 오라클 계정을 생성해서 프로젝트를 하자
Spring MVC 프로젝트 생성
- 도메인명 역순으로 지정해야한다
- com.myhome.spring 을 top-level 패키지로 지정
프토젝트 테스트
- 프로젝트 오른 마우스 -> Run As -> Run on Server
- 잘 실행되었음을 확인 가능
커넥션 생성, 테이블 생성하기
- webapp 폴더 하위에 sql 폴더 추가하고, 안에 SQL 파일 myboard.sql 생성
- 다음으로 커넥션을 연결해야함
- 커넥션이 생성되었다!
- Connection profile 도 설정한다
- 다음은 테이블 생성 위해 myboard.sql 작성
-- 게시판
select * from tab;
select * from seq;
select * from myboard;
-- 테이블명 : myboard
create table myboard(
no number primary key,
writer varchar2(20),
passwd varchar2(20),
subject varchar2(50),
content varchar2(100),
readcount number,
register date );
-- 시퀀스명 : myboard_seq
create sequence myboard_seq;
- 테이블과 시퀀스 생성하기
- 테이블 및 시퀀스 생성 확인
환경설정 파일 세팅
+ 환경 설정 파일 작성(세팅) 순서 1) pom.xml 2) web.xml 3) servlet-context.xml 4) configuration.xml 5) board.xml 6) root-context.xml
pom.xml 파일 세팅
- Maven 의 환경설정 파일이다
- 잘 실행되는 내용을 가져와서 복붙하면 된다, 프로젝트 myBatis2 의 pom.xml 을 그대로 가져와서 복붙
- pom.xml 은 프로젝트 바로 하위에 있으므로 프로젝트를 선택하고 붙여넣기 (overwrite)
- 이 폼에서 넘어가는 내용은 writer, passwd, subject, content 이다
- 입력하고 "글작성" 클릭시 "boardwrite.do" 로 넘어간다, Controller -> Service -> DAO (Mapper 파일) 으로 가야한다
- Controller 클래스 BoardController.java 에서 "boardwrite.do" 요청 부분을 처리하자
- 아래 내용을 작성
// 글 작성
@RequestMapping("boardwrite.do")
public String boardwrite(Board board, Model model) {
int result = bs.insert(board);
model.addAttribute("result", result);
return "board/insertresult";
}
- 폼에서 넘어온 값을 DTO 객체로 한번에 받기 위해 @ModelAttribute 사용
- 메소드 명은 요청 이름값과 같게 맞췄다
- 글을 작성(insert) 한 후 성공 / 실패를 View 에서 처리하기위해 어떠한 값들을 가져갈 것이므로 Model 객체 선언해서 받기
- Service 객체 bs 를 사용해서 Service 클래스의 insert() 메소드를 호출, 이때 삽입할 데이터인 객체 board 를 가져감, 리턴자료형은 int 이다
- Service 클래스 BoardService.java 에서 insert() 메소드를 생성해야한다
public int insert(Board board) {
return dao.insert(board);
}
- Controller 에서 이 메소드를 호출해야하므로 public, 리턴자료형은 int, 메소드 명은 insert(), 매개변수 자료형은 Board
- DAO 의 insert() 를 호출
- 이 코드를 복사 후 DAO 클래스 BoardDao.java 로 가서 복붙 하고 수정
- BoardDao.java 에서 insert() 메소드 생성
public int insert(Board board) {
return session.insert("insert", board);
}
- 이 insert() 가 실제 SQL문을 수행하고 성공시 1 을 반환함
- Mapper 파일인 Board.xml 에 id 가 "insert" 인 insert (글 작성) 문을 생성해야함
<!-- 글 작성 -->
<insert id="insert" parameterType="board">
insert into myboard values(myboard_seq.nextval,#{writer},
#{passwd},#{subject},#{content},0,sysdate)
</insert>
- 전달되는 값이 DTO Board 객체 board 이므로 DTO Board 의 alias 인 "board" 를 parameterType 에 설정
- no 컬럼은 sequence 인 myboard_seq 로 세팅
- 앞에서 객체 board 가 넘어왔으므로 #{writer}, #{passwd}, #{subject}, #{content} 로 세팅
- JSTL core 라이브러리 사용하기 위해 core 라이브러리 불러오는 코드 한 줄 추가
- 글 작성 성공하면 목록 페이지로 갈 것이므로 "boardlist.do" 요청하기
- 글 작성 실패시 이전 페이지인 글 작성 폼으로 돌아감
- index 파일을 실행시켜서 글 작성폼에서 글을 작성해보자
+ 글 수정 / 삭제 시 비밀번호가 맞아야 삭제되도록 하기 위해 비밀번호를 입력받는 것임
- "boardlist.do" 요청 처리를 하지 않았으므로 오류가 뜬다
- Controller 클래스 BoardController.java 에서 "boardlist.do" 요청을 처리하는 코드를 작성하자 (미완성, 수정 전)
// 글 목록
@RequestMapping("boardlist.do")
public String boardlist(HttpServletRequest request, Model model) {
int page = 1; // 현재 페이지 번호
int limit = 10; // 한 화면에 출력할 데이터 갯수
if(request.getParameter("page") != null) {
page = Integer.parseInt(request.getParameter("page"));
}
int startRow = (page - 1) * limit + 1;
int endRow = page * limit;
int listcount = bs.getCount(); // 총 데이터 갯수
System.out.println("listcount : " + listcount);
return "board/boardlist";
}
- 기본변수, 파생변수를 설정해서 DB에서 목록을 구해오고, 페이징 처리를 하고, 그걸 View 에 뿌려야함
- request 객체를 매개변수로 부터 받는다, 이렇게 매개변수에 선언하면 자동으로 request 객체를 받아옴
- 결과를 View 페이지에 뿌려야하므로 Model 객체도 매개변수에 선언해서 받아온다
1. 기본변수 page : 현재 페이지를 저장할 변수
2. 기본변수 limit : 한 화면에 출력할 데이터 개수를 저장하는 변수
3. 파생변수 starRow, endRow : page, limit 을 이용해서 구해준다, page, limit 로 생성 가능하므로 굳이 생성하지 않아도 된다
4. 기본변수 listcount : 총 데이터 갯수를 저장, DB 와 연동해서 count(*) 로 가져올 것
<총 데이터 개수 가져오기>
- Service 클래스의 getCount() 를 호출해서 리턴받는다 * 아래에서 Service 에서 getCount() 를 생성함
- 나머지 부분은 아래에서 다시 완성
- Service 클래스 BoardService.java 에서 getCount() 메소드를 생성해야함
public int getCount() {
return dao.getCount();
}
- DAO 클래스 BoardDao.java 에서 getCount() 메소드 생성하기
public int getCount() {
return session.selectOne("count");
}
- 그룹함수는 결과가 1개이므로 selectOne() 메소드 사용, id 가 "count" 인 SQL문 불러오기
- Mapper 파일 Board.xml 에서 id 가 "count" 인 SQL문 생성
<!-- 총 데이터 갯수 -->
<select id="count" resultType="int">
select count(*) from myboard
</select>
- 돌려주는 값이 총 데이터 갯수이므로 resultType 은 "int"
- 총 데이터 갯수를 가져왔는지 확인하기 위해 현재 상태에서 index 파일에서 "boardlist.do" 로 요청해보자
- index 파일 실행 하고 콘솔창 보기
- listcount 값이 찍혀 나왔다, 글을 하나 작성했었으므로 listcount : 1 이 맞다
- 이제 목록을 가져오는 작업을 Controller 부터 처리하자
- Controller 클래스 BoardController.java 에서 "boardlist.do" 요청을 처리하는 코드를 작성하자 (이어서, 완성, 추가되었다)
// 글 목록
@RequestMapping("boardlist.do")
public String boardlist(HttpServletRequest request, Model model) {
int page = 1; // 현재 페이지 번호
int limit = 10; // 한 화면에 출력할 데이터 갯수
if(request.getParameter("page") != null) {
page = Integer.parseInt(request.getParameter("page"));
}
int startRow = (page - 1) * limit + 1;
int endRow = page * limit;
int listcount = bs.getCount(); // 총 데이터 갯수
System.out.println("listcount : " + listcount);
List<Board> boardlist = bs.getBoardList(page); // 게시판 목록
System.out.println("boardlist : " + boardlist);
// 총 페이지
int pageCount = listcount / limit + ((listcount % limit == 0) ? 0 : 1);
int startPage = ((page - 1) / 10) * limit + 1; // 1, 11, 21...
int endPage = startPage + 10 - 1; // 10, 20, 30...
if(endPage > pageCount) endPage = pageCount;
model.addAttribute("page", page);
model.addAttribute("listcount", listcount);
model.addAttribute("boardlist", boardlist); // 리스트
model.addAttribute("pageCount", pageCount);
model.addAttribute("startPage", startPage);
model.addAttribute("endPage", endPage);
return "board/boardlist";
}
<목록 가져오기>
- MyBatis 로 처리하면 DAO -> Mapper 파일로 갈때 단 하나의 값만 전달가능하다
- limit 은 고정되어있고, startRow, endRow 는 나중에 페이지 번호 page 로 계산 가능하므로 페이지번호 page 만 넘기기
+ 또는 map 객체에 키밸류 형태로 저장하는 방법도 있다
- getBoardList() 에서 글들이 저장된 리스트를 반환하도록 할 것 * 아래에서 설명
<돌아온 후>
- getBoardList() 에서 돌아온 후 콘솔창 출력해보면 boadlist 에 내가 썼던 글의 주소가 출력된다
5. 파생변수 pageCount : 총 페이지 수를 구함, listcount 와 limit 사용
6. 파생변수 startPage & endPage : 각(현재) 블럭의 시작 페이지, 끝 페이지, page 와 limit 사용
- 이후 기본변수, 파생변수, 가져온 목록(List) 들을 Model 객체에 저장하고 board/boardlist.jsp 로 이동
- Service 클래스 BoardService.java 에서 목록을 가져오는 메소드 getBoardList() 를 생성하자
public List<Board> getBoardList(int page) {
return dao.getBoardList(page);
}
- DAO 클래스 BoardDao.java 에서 getBoardList() 를 작성하자
public List<Board> getBoardList(int page) {
return session.selectList("list", page);
}
- 여러개의 글(DTO 객체) 을 가져올 것이므로 selectList() 메소드 사용
- 페이지 번호 page 를 전달함, 받는 값은 DTO 객체들이 저장된 리스트이다
- Mapper 파일 Baord.xml 에서 id 가 "list" 인 SQL문 작성
<!-- 글 목록 -->
<!-- > : > , < : < -->
<select id="list" parameterType="int" resultType="board">
<![CDATA[
select * from (select rownum rnum, board.* from (
select * from myboard order by no desc) board )
where rnum >= ((#{page}-1) * 10 + 1) and rnum <= (#{page} * 10)
]]>
</select>
- 페이지 번호가 넘어오므로 parameterType 은 "int" , 돌려주는 값은 DTO 가 저장된 리스트 이므로 resultType 은 "board"(DTO alias)
- limit 은 10으로 고정되어있으므로 10 으로 쓰고, startRow, endRow 는 페이지 번호 page 로 계산 가능해서 사용하기
+ <. > 인식을 못하므로 > , $lt; 사용, 또는 <![CDATA[ ]]> 로 전체 SQL문으로 감싸주면 부등호 기호 <, > 를 인식함
- View 페이지를 보기 전에 페이징 처리를 확인하기 위해 myboard.sql 에서 데이터를 강제로 많이 삽입하자
insert into myboard values(myboard_seq.nextval,'Lay','1234','Practice1',
'Content',0,systdate)
- View 페이지 board/boardlist.jsp 를 생성해서 Contorller 에서 Model 객체에 저장된 값들을 활용해서 목록을 출력하자
- boardlist.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>게시판 목록</title>
</head>
<body>
<a href="boardform.do">글작성</a> <br>
글갯수 : ${listcount }
<table border=1 align=center width=700>
<caption>게시판 목록</caption>
<tr>
<th>번호</th>
<th>제목</th>
<th>작성자</th>
<th>날짜</th>
<th>조회수</th>
</tr>
<!-- 화면 출력 번호 -->
<c:set var="num" value="${listcount - (page - 1) * 10}"/>
<c:forEach var="b" items="${boardlist}">
<tr>
<td>${num}
<c:set var="num" value="${num-1}"/>
</td>
<td>
<a href="boardcontent.do?no=${b.no}&page=${page}">${b.subject}</a>
</td>
<td>${b.writer}</td>
<td>
<fmt:formatDate value="${b.register}"
pattern="yyyy-MM-dd HH:mm:ss"/>
</td>
<td>${b.readcount}</td>
</tr>
</c:forEach>
</table>
<br>
<!-- 페이지 처리 -->
<center>
<c:if test="${listcount > 0}">
<!-- 1페이지로 이동 -->
<a href="boardlist.do?page=1" style="text-decoration:none"> << </a>
<!-- 이전 블럭으로 이동 -->
<c:if test="${startpage > 10}">
<a href="boardlist.do?page=${startPage - 10}">[이전]</a>
</c:if>
<!-- 각 블럭에 10개 페이지 출력 -->
<c:forEach var="i" begin="${startPage}" end="${endPage}">
<c:if test="${i == page}"> <!-- 현재 페이지 -->
[${i}]
</c:if>
<c:if test="${i != page}"> <!-- 현재 페이지가 아닌 경우-->
<a href="boardlist.do?page=${i}">[${i}]</a>
</c:if>
</c:forEach>
<!-- 다음 블럭으로 이동 -->
<c:if test="${endPage < pageCount}">
<a href="boardlist.do?page=${startPage + 10}">[다음]</a>
</c:if>
<!-- 마지막 페이지로 이동 -->
<a href="boardlist.do?page=${pageCount}" style="text-decoration:none"> >> </a>
</c:if>
</center>
</body>
</html>
- JSTL core 라이브러리, 국제화 라이브러리를 불러오기
7. 파생변수 num : 화면 출력 번호, 코어 라이브러리 set 태그 사용해서 생성했음, listcount, page 를 사용해서 생성
- forEach 태그 items 안에 목록 리스트인 ${boardlist} 를 써서 하나씩 글을 가져오기
- forEach 루프가 돌아갈때마다 num 을 1씩 감소시키기, 이때 코어 라이브러리 set 태그 사용해서 재정의 하는 방식