20220831_Wed
: 컨트롤러에 매개변수에 속해있으면서 데이터를 받기위해있는 객체
- 1.넘어오는 데이터를 받는 역할
- 2.데이터를 다음 페이지에 넘기지 않아도(model.addattribute() 없어도) 자동으로 넘어감
- 단, 데이터가 넘어갈 때의 이름은 클래스명에서 앞글자만 소문자로 바뀐 이름으로만 넘어간다(이와 다를시 오류발생)
- ex1)
: 아래 코드에서 (BoardVO boardVO) 가 커맨드 객체다//글등록 @GetMapping("/regBoard") public String regBoard(BoardVO boardVO) { boardService.insertBoard(boardVO); return "board/reg_board_result"; }
- ex2)
: 우리가 작성한 변수명으로 넘어가는 것이아니다!!!
아래 코드에서 (BoardVO board)라고 변수명(board)을 작성해도 board로 넘어가는 것이 아니라, 클래스형 자료형 BoardVO 에서 앞글자만 바뀐 boardVO로만 넘겨진다.//글등록 @GetMapping("/regBoard") public String regBoard(BoardVO board) { boardService.insertBoard(board); return "board/reg_board_result"; }
- a태그로 데이터 넘길 때 문법
<a th:href="@{이동할경로(name='java',age=20)}" th:text="${board.title}"></a>
- 실제 board_list.html 에서 제목클릭시 상세보기 페이지 이동 시 적용하면..
<a th:href="@{/board/detail(boardNum=${board.boardNum})}" th:text="${board.title}"></a>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<table border="1">
<thead>
<tr>
<td>게시글 번호</td>
<td>제목</td>
<td>작성자</td>
<td>작성일</td>
<!-- 리스트크기 데이터 갖고오는 방법 2가지-->
<!-- <td th:text="${#lists.size(boardList)}"></td>
<td th:text="${boardList.size()}"></td> -->
</tr>
</thead>
<tbody>
<th:block th:if="${#lists.size(boardList) == 0}">
<tr>
<td colspan="4">게시글이 없습니다.</td>
</tr>
</th:block>
<th:block th:unless="${#lists.size(boardList) == 0}">
<tr th:each="board : ${boardList}"><!-- 컨트롤러에서 넘긴 그대로:boardList -->
<td th:text="${board.boardNum}"></td>
<td>
<a th:href="@{/board/detail(boardNum=${board.boardNum})}" th:text="${board.title}"></a>
</td>
<td th:text="${board.writer}"></td>
<td th:text="${board.createDate}"></td>
</tr>
</th:block>
</tbody>
</table>
<div align="center">
<!-- location.href 이동시, /board/... 식으로(컨트롤러이동) 작성해야한다! -->
<input class="btn" type="button" value="글쓰기" onclick ="location.href='/board/regBoard';">
</div>
</body>
</html>
실습내용(어제 계속..)
- 게시글 상세보기 기능 구현(a태그)
- 게시글 수정/삭제/뒤로가기 기능 구현
<생성파일 목록>
- BoardController
- BoardVO
- html파일:
- boardList.html
- reg_board.html
- reg_board_result.html
- detail_board.html
- update_board.html
- update_board_result.html
- delete_board.html
- board-mapper
- boardService
- boardServiceImpl
@Controller
@RequestMapping("/board")// "/board"로 시작하면 무조건 컨트롤러 실행하겠다
public class Boardcontroller {
//boardService 라는 이름으로 만들어진 개체를 로드해서
//"의존성 주입"을 실행한다
//@Resource는 @autoWired와 비슷하지만 자료형이 같은 객체가 아니라
//name값이 같은 변수명을 가져와서 데이터를 넣어준다
//1-1.의존성 주입
@Resource(name = "boardService")//boardService라는 이름의 객체를 가져오겠습니다!
private BoardService boardService;//스프링에서는 선언만!
//게시글 목록 페이지
@GetMapping("/list")
public String selectBoardList(Model model) {
//1.게시글 목록 조회(한줄요약)
model.addAttribute("boardList",boardService.selectBoardList());
return "board/board_list";//어디로 이동할래?
}
//(양식)글등록하는 페이지로 이동: Form양식페이지로 안가는 이유가 있다!!
///regBoard 컨트롤러로 넘어올 때 중복인데 어노테이션이 다르기때문 괜찮다!
@GetMapping("/regBoard")//a태그,href일경우...
public String regBoard() {
return "board/reg_board";
}
//글 등록
@PostMapping("/regBoard")//form태그로 넘어올땐!
//커맨드 객체(컨트롤러에 매개변수에 속해있으면서 데이터를 받기위해있는 객체)
//1.넘어오는 데이터를 받는 역할
//2.데이터를 다음 페이지에 넘기지 않아도 자동으로 넘어감
public String regBoard(BoardVO boardVO) {
boardService.insertBoard(boardVO);
return "board/reg_board_result";
}
//상세보기
//넘어오는 데이터의 이름과 매개변수의 이름을 일치시키면
//매개변수에 자동으로 데이터가 넘어온다
//매개변수에 @RequestParam 어노테이션사용하면
//넘어오는 데이터에 관한 설정을 할 수 있다.
//속성 1. required : true/false 값이 올 수 있고,
//넘어오는 데이터의 필수여부를 지정한다. (true면 필수,false면 뭐든 노상관)
//속성 2. defaultValue :넘어오는 데이터가 없을 때, 기본값을 지정
//속성 3. name :넘어오는 데이터의 이름과 매개변수의 이름이 불일치할 때 데이터를 바인딩 시켜준다.
@GetMapping("/detail")
public String detailBoard(@RequestParam(required = false) int boardNum, Model model) {
model.addAttribute("board",boardService.selectDetailBoard(boardNum));
return "board/detail_board";
}
//글수정 양식페이지로 이동
@GetMapping("/update")
public String update(int boardNum,Model model) {
//실제 글수정 전, 먼저 상세정보 조회 먼저 해야한다
model.addAttribute("board",boardService.selectDetailBoard(boardNum));
return "board/update_board";
}
//글 수정
@PostMapping("/update")
public String update( BoardVO boardVO) {
boardService.updateBoard(boardVO);
return "redirect:/board/detail?boardNum=" + boardVO.getBoardNum();
}
//글 삭제
@GetMapping("/delete")
public String delete(int boardNum) {
boardService.deleteBoard(boardNum);
return "redirect:/board/list";
}
}
package kh.study.board.vo;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
//@Data
@Getter
@Setter
@ToString
public class BoardVO {
//lombok 어노테이션(위에) 을 이용해 getter,setter를 편하게 만들수있다
private int boardNum;
private String title;
private String content;
private String writer;
private String createDate;
}
a 태그로 넘길 때
<a th:href="@{이동할경로(name='java',age=20)}" th:text="${board.title}"></a>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<style type="text/css">
table{
width: 800px;
margin: 0 auto;
border-collapse: collapse;
border: 1px solid black;
}
table>thead>tr:first-child{
margin-bottom: 10px;
}
tr,td{
border-collapse: collapse;
border: 1px solid black;
}
.btn{
text-align: center;
margin-top: 20px;
}
</style>
</head>
<body>
<table border="1">
<thead>
<tr>
<td>게시글 번호</td>
<td>제목</td>
<td>작성자</td>
<td>작성일</td>
<!-- 리스트크기 데이터 갖고오는 방법 2가지-->
<!-- <td th:text="${#lists.size(boardList)}"></td>
<td th:text="${boardList.size()}"></td> -->
</tr>
</thead>
<tbody>
<th:block th:if="${#lists.size(boardList) == 0}">
<tr>
<td colspan="4">게시글이 없습니다.</td>
</tr>
</th:block>
<th:block th:unless="${#lists.size(boardList) == 0}">
<tr th:each="board : ${boardList}"><!-- 컨트롤러에서 넘긴 그대로:boardList -->
<td th:text="${board.boardNum}"></td>
<td>
<!-- a태그로 데이터 넘길 때 문법 <a th:href="@{이동할경로(name='java',age=20)}" th:text="${board.title}"></a> -->
<a th:href="@{/board/detail(boardNum=${board.boardNum})}" th:text="${board.title}"></a>
</td>
<td th:text="${board.writer}"></td>
<td th:text="${board.createDate}"></td>
</tr>
</th:block>
</tbody>
</table>
<div align="center">
<!-- location.href 이동시, /board/... 식으로(컨트롤러이동) 작성해야한다! -->
<input class="btn" type="button" value="글쓰기" onclick ="location.href='/board/regBoard';">
</div>
</body>
</html>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"><!-- jsp에서 foreach문 사용할때 lib태그 사용하는 것과 같다.앞으로 html 만들때는 사용해야한다. 원래 html만들때 next누르고 별도로 생성한 htnl_thymeleaf를 선택하면 자동생성된다-->
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="/board/regBoard" method="post"><!--form 하면 반드시 submit 필요 (onclick삭제) -->
<table>
<tr>
<td>제목</td>
<td><input name="title" type="text" ></td>
</tr>
<tr>
<td>작성자</td>
<td><input name="writer" type="text" ></td>
</tr>
<tr>
<td>내용</td>
<td><textarea rows="3" cols="50" name="content"></textarea></td>
</tr>
</table>
<div align="center">
<input type="submit" value="글등록">
<input type="button" value="뒤로가기" onclick="location.href='/board/list';">
</div>
</form>
</body>
</html>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"><!-- jsp에서 foreach문 사용할때 lib태그 사용하는 것과 같다.앞으로 html 만들때는 사용해야한다. 원래 html만들때 next누르고 별도로 생성한 htnl_thymeleaf를 선택하면 자동생성된다-->
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<script type="text/javascript">
alert('글 등록되었습니다.');
location.href='/board/list';
</script>
<!-- 컨트롤러에서 따로 데이터를 넘겨라는 코드가 없어도 자동으로 데이터가 받아짐 -->
<!--
데이터 자동으로 넘겨질 때 :
<span th:text="${boardVO.title}" ></span>
데이터 넘겨지지 않을 때:
<span th:text="${board.title}" ></span>
-->
</body>
</html>
데이터가져가면서 페이지 이동 할 때
- 스프링에서 타임리프로 데이터넘기면서 페이지이동 방법
<input type="button" class="btn" value="삭제1" th:onclick="'location.href=\'' + @{/board/delete(boardNum=${board.boardNum})}+'\''" >
- 버티컬바 사용시 더 편리하게 코드작성 가능
<input type="button" class="btn" value="삭제2" th:onclick="|location.href='@{/board/delete(boardNum=${board.boardNum})}'|">
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h2>게시글 상세보기 페이지</h2>
<table border="1">
<tr>
<td>글 번호</td>
<td th:text="${board.boardNum}"></td>
</tr>
<tr>
<td>글 제목</td>
<td th:text="${board.title}"></td>
</tr>
<tr>
<td>작성자</td>
<td th:text="${board.writer}"></td>
</tr>
<tr>
<td>작성일</td>
<td th:text="${board.createDate}"></td>
</tr>
<tr>
<td>글 내용</td>
<td th:text="${board.content}"></td>
</tr>
</table>
<div>
<input type="button" class="btn" value="수정" th:onclick="|location.href='@{/board/update(boardNum=${board.boardNum})}'|">
<input type="button" class="btn" value="삭제" th:onclick="|location.href='@{/board/delete(boardNum=${board.boardNum})}'|">
<input type="button" class="btn" value="돌아가기" onclick ="location.href='/board/list';">
<!-- 삭제버튼 설명 -->
<!-- 삭제 1 : 이전 jsp 방식일때 -->
<!-- <input type="button" class="btn" value="삭제1" >-->
<!-- 삭제 2 : 숫자10 으로 바로넘길때 오류발생안남 -->
<!-- <input type="button" class="btn" value="삭제2" >
위의 숫자 10으로 넘어가는 것처럼 ${board.baordNum}숫자로 넘겨지는게 아니라 문자그대로만 넘겨져 오류발생
<input type="button" class="btn" value="삭제2" >
삭제3 : 타임리프방식(스프링) \ 이스케이프문자 사용시 ''홀따옴표도 글자취급가능
<input type="button" class="btn" value="삭제3" th:onclick="'location.href=\'' + @{/board/delete(boardNum=${board.boardNum})}+'\''" >
<!-- 스프링에서 타임리프로 데이터넘기면서 페이지이동 방법 -->
<!-- <input type="button" class="btn" value="삭제1" th:onclick="'location.href=\'' + @{/board/delete(boardNum=${board.boardNum})}+'\''" >
버티컬바 || 사용시
<input type="button" class="btn" value="삭제2" th:onclick="|location.href='@{/board/delete(boardNum=${board.boardNum})}'|">
-->
</div>
</body>
</html>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h2>글 수정</h2>
<form action="/board/update" method="post">
<input type="hidden" name="boardNum" th:value="${board.boardNum}">
<table>
<tr>
<td>글 번호</td>
<td><span th:text="${board.boardNum}"></span></td>
</tr>
<tr>
<td>작성일</td>
<td><span th:text="${board.createDate}"></span></td>
</tr>
<tr>
<td>작성자</td>
<td><input type="text" name="writer" th:value=${board.writer}></td>
</tr>
<tr>
<td>글 제목</td>
<td><input type="text" name="title" th:value="${board.title}"></td>
</tr>
<tr>
<td>글 내용</td>
<td><textarea rows="5" cols="100" name="content" th:value="${board.content}"></textarea> </td>
</tr>
</table>
<div>
<input type="submit" class="btn" value="수정완료" >
</div>
</form>
</body>
</html>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"><!-- jsp에서 foreach문 사용할때 lib태그 사용하는 것과 같다.앞으로 html 만들때는 사용해야한다. 원래 html만들때 next누르고 별도로 생성한 htnl_thymeleaf를 선택하면 자동생성된다-->
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<script type="text/javascript">
alert('수정 완료!');
location.href='/board/list';
</script>
</body>
</html>
<?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="boardMapper">
<resultMap type="kh.study.board.vo.BoardVO" id="board">
<id column="BOARD_NUM" property="boardNum"/>
<result column="TITLE" property="title"/>
<result column="WRITER" property="writer"/>
<result column="CONTENT" property="content"/>
<result column="CREATE_DATE" property="createDate"/>
</resultMap>
<!-- 목록조회 -->
<select id="selectBoardList" resultMap="board">
SELECT BOARD_NUM
, TITLE
, WRITER
, CREATE_DATE
FROM SPRING_BOARD
ORDER BY BOARD_NUM DESC
</select>
<!-- 게시글등록 -->
<insert id="insertBoard">
INSERT INTO SPRING_BOARD (
BOARD_NUM
, TITLE
, CONTENT
, WRITER
) VALUES(
BOARD_NUM_SEQ.NEXTVAL
, #{title}
, #{content}
, #{writer}
)
</insert>
<!-- 상세보기 -->
<select id="selectDetailBoard" resultMap="board">
SELECT BOARD_NUM
, TITLE
, WRITER
, CONTENT
, TO_CHAR(CREATE_DATE,'YYYY-MM-DD') AS CREATE_DATE
FROM SPRING_BOARD
WHERE BOARD_NUM = #{boardNum}
</select>
<!-- 글 수정 -->
<update id="updateBoard">
UPDATE SPRING_BOARD
SET TITLE = #{title}
, WRITER = #{writer}
, CONTENT = #{content}
WHERE BOARD_NUM = #{boardNum}
</update>
<!-- 글 삭제 -->
<delete id="deleteBoard">
DELETE FROM SPRING_BOARD
WHERE BOARD_NUM = #{boardNum}
</delete>
</mapper>
package kh.study.board.service;
import java.util.List;
import kh.study.board.vo.BoardVO;
public interface BoardService {
//게시글 목록조회
List<BoardVO> selectBoardList();
//글쓰기 등록
void insertBoard(BoardVO boardVO);
//상세보기
BoardVO selectDetailBoard(int boardNum);
//글 수정
void updateBoard(BoardVO boardVO);
//글 삭제
void deleteBoard(int boardNum);
}
package kh.study.board.service;
import java.util.List;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import kh.study.board.vo.BoardVO;
@Service("boardService")//괄호안에 빈값이면 클래스명 소문자로 BoardServiceImpl 객체자동생성되기 때문에
public class BoardServiceImpl implements BoardService {
//쿼리 실행하는 객체
// SqlSessionFactory sqlSessionFactory = SqlSessionManager.getSqlSession();
// SqlSession sqlSession;//생성이 아닌 선언만
//@Autowired어노테이션은 미리 생성한 객체를 의존성 주입 시킬 때 사용한다.
//아래코드가 진행이 된다는 것은, 프로젝트 실행 시
// 스프링이 내부적으로 sqlSessionTemplate 자료형으로 객체를 미리 만들어 놓기 때문에 사용가능하다.
//@Autowired는 그래서 동일한 자료형으로 객체가 미리 2개 이상 생성되어있다면,
//프로젝트 실행과 동시에 오류 발생
//-> DB로 이동
@Autowired//자동연결 (기준 자료형이 같은 객체만)
SqlSessionTemplate sqlSession;
//게시글 목록조회
@Override
public List<BoardVO> selectBoardList() {
//원래 이클립스에서 하는 방식
// List<BoardVO> list = sqlSession.selectList("boardMapper.selectBoardList");
// return list;
// sqlSession.commit();(스프링은 자동 커밋)
//->(스프링) 그래서 이제 부터 한 줄로 작성 가능해짐
return sqlSession.selectList("boardMapper.selectBoardList");
}
//글쓰기 등록
@Override
public void insertBoard(BoardVO boardVO) {
sqlSession.insert("boardMapper.insertBoard",boardVO);
}
//상세보기
@Override
public BoardVO selectDetailBoard(int boardNum) {
return sqlSession.selectOne("boardMapper.selectDetailBoard",boardNum);
}
//글 수정
@Override
public void updateBoard(BoardVO boardVO) {
sqlSession.update("boardMapper.updateBoard",boardVO);
}
//글 삭제
@Override
public void deleteBoard(int boardNum) {
sqlSession.delete("boardMapper.deleteBoard",boardNum);
}
}
실습내용
- 게시판 게시글 검색기능 구현
<순서>- ① board_list.html 에서 테이블 위에 검색기능 추가
- ② board-mapper에서 쿼리문 수정 (목록조회)
- ③ baordService/ boardServiceImpl 각각 매개변수(String title) 수정
- ④ baordController 목록조회 메소드수정
<!-- 검색기능 구현 -->
<form action="/board/list" method="post">
<input type="text" name="title">
<input type="submit" value="검색"><br>
</form>
쿼리문 작성시, 바로 test 값을 사용해도 되는 이유?
- 빈값채울 때 빈값채우기 위해서 넘어오는 데이터 : _parameter
: mybatis 에서 title 그래로 작성하면 getter인 title.getTitle()를 호출한다.
getter는 빈값이 보이면 자동으로 데이터를 넘겨주는데 목록조회 쿼리문에서는 title 하나의 빈값만 존재하므로 _parameter 값을 사용한다
<select id="selectBoardList" resultMap="board">
SELECT BOARD_NUM
, TITLE
, WRITER
, CREATE_DATE
FROM SPRING_BOARD
<if test="_parameter != null and !_parameter.equals('')">
WHERE TITLE LIKE '%'||#{title}||'%'
</if>
ORDER BY BOARD_NUM DESC
</select>
③ baordService/ boardServiceImpl 각각 매개변수(String title) 수정
//게시글 목록조회
List<BoardVO> selectBoardList(String title);
//게시글 목록조회
@Override
public List<BoardVO> selectBoardList(String title) {
return sqlSession.selectList("boardMapper.selectBoardList",title);
}
④ baordController 목록조회 메소드수정
Q. title 데이터 값을 넘겨받을 때, title 값이 목록조회 쿼리문에서 처럼 언제 null값이고 빈값인지 어떻게 구분하는가?
:System.out.println("title="+ title);
으로 각각의 상황에 따라 콘솔창 확인하여 구분한다
- 주소창에 기본경로
http://localhost:8081/board/list
페이지 바로 입력시
-> title = null 값- 게시글 목록페이지에서 빈값으로 검색버튼 클릭시
-> title = '' 빈값
//게시글 목록 페이지
@RequestMapping("/list")//get,post 모두 받는 어노테이션
public String selectBoardList(Model model,String title) {
System.out.println("title="+ title);
model.addAttribute("boardList",boardService.selectBoardList(title));
return "board/board_list";//어디로 이동할래?
}