Spring 실습

Spring 실습 3일차 (Book list, detail, edit, delete, search)

choco2706 2024. 4. 24. 13:42

오늘은 화면에 책 리스트를 띄워볼 예정이다.

URL을 localhost/list 로 들어가면 데이터베이스에 담겨있는 책들의 정보가 리스트로 출력되도록. 

그리고 detail, edit, delete 그리고 search 까지 해볼 예정이다.

BookController

일단 어제 만든 BookController에서 코드를 추가해보자. 완성본은 아니고 매핑이 되는지 확인용도

/*
	    요청URI : /list?keyword=알탄 or /list or /list?keyword=
	    요청파라미터 : keyword=알탄
	    요청방식 : get
	    
	  required=false : 선택사항. 파라미터가 없어도 무관
    */
	@RequestMapping(value="/list", method=RequestMethod.GET)
	public ModelAndView list(ModelAndView mav) {
		log.info("list에서 왔다 : " + mav);
		
		// Model : 데이터
		
		// View : jsp
		// forwarding
		mav.setViewName("book/list");
		
		return mav;
	}

 

어제와 다른 점은 ModelAndView가 파라미터에서 선언되었다는 점이다.

 

또 하나 setViewName은 jsp의 경로를 적어넣어야 하는데 list 뒤에 .jsp를 붙히지 않았고, book 앞에 경로를 설정하지 않았다.

 

이게 가능한 이유는 

servlet-context.xml

 

 

이 부분 덕분에 book/list 앞 뒤로 있어야 할 경로가 자동으로 들어가기 때문이다.

 

list.jsp

그 다음 list.jsp를 만들어볼껀데 JSP 템플릿을 JSTL이 포함되어있는 템플릿을 하나 만들도록 하자

File name은 list로 해주자

 

위 경로와 같이 jsp를 만들어주고 finish가 아닌 Next를 눌러준다.

 

 

오른쪽 하단에 JSP Template을 눌러주고


New 버튼 클릭 

 

위와 같이 설정한다.

 

더보기
<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
${cursor}
</body>
</html>

 

그럼 위와 같은 기본적으로 JSTL이 담긴 JSP 템플릿이 만들어진다. 그대로 Finish를 눌러주자

 

h3태그를 사용했다.

 

다행히 매핑이 잘 되서 jsp를 불러오고 있는 모습이니 Controller로 다시 넘어가 이어서 작성해보자

 

Controller 이어서 작성

@RequestMapping(value="/list", method=RequestMethod.GET)
	public ModelAndView list(ModelAndView mav) {
		log.info("list에서 왔다 : " + mav);
		
        // 도서 목록
		List<BookVO> bookVOList = this.bookService.list();
		log.info("list->bookVOList : " + bookVOList);
		
		// Model : 데이터
		
		
		// View : jsp
		// forwarding
		mav.setViewName("book/list");
		
		return mav;
	}

 

.list()에 오류가 난다고 뜰테지만 bookService에 list메소드가 없기 때문이니

어제와 마찬가지로 f2룰 눌러 BookService 인터페이스에 메소드를 만들어주자

 

이어서 쭉 Impl과 Dao까지 설정을 해주자

Impl
Repository

 

책 리스트를 SQL에서 Select문으로 가져올것이고, 1개 이상이기 때문에 SelectList로 연결되어야 한다.

"book.selectList"는 book이라는 namespase에 select태그의 id값이 list라는 태그로 연결한다는 뜻

 

이제 book_sql.xml로 넘어가준다.

book_SQL.xml

<select id="list" resultType="bookVO">
	   select book_id title, category, price, insert_date 
		from book 
		order by book_id desc
   </select>

 

위 코드는 SQL의 book 테이블에서 book_id, title, categort, price, insert_data를 "역순(최신순)"으로 뽑아 내 bookVO에 담아 리턴해준다.

 

이제 최종적으로 Controller를 완성하자

@RequestMapping(value="/list", method=RequestMethod.GET)
	public ModelAndView list(ModelAndView mav) {
		log.info("list에서 왔다 : " + mav);
		
		List<BookVO> bookVOList = this.bookService.list();
		log.info("list->bookVOList : " + bookVOList);
		
		// Model : 데이터
		mav.addObject("bookVOList", bookVOList);
		
		// View : jsp
		// forwarding
		mav.setViewName("book/list");
		
		return mav;
	}

 

JSP도 마저 설정을 해줘야 한다. 값을 불러와야 하기 때문

 

<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<title>도서 목록</title>
</head>
<body>
<h3>도서 목록</h3>
<!-- mav.addObject("bookVOList", bookVOList); -->
<p>${bookVOList}</p>
</body>
</html>

 

SQL에서 출력한 책 정보
JSP로 출력한 책 정보

 

SQL과 JSP에서 출력되는 정보가 동일하게 출력된다.

 

SQL에 책 정보를 대량으로 한번에 넣는 코드

/
DECLARE
BEGIN
for i in 1..127 loop
    insert into book(book_id, title, category, price, insert_date)
    values (
        (select nvl(max(book_id),0)+1 from book), -- 마지막 번호에서 +1
        '제목'||i, -- 제목 + i(반복)
        '소설',
        trunc(dbms_random.value(10000,50000)), -- 만원~5만원
        sysdate
    );
    end loop;
END;
/

 

1~127번 반복되는 SQL/PL 코드이다. 여기선 카테고리명을 소설로 한정했다. 

마지막엔 항상 commit을 해주자.

 

그럼 이제 뭘 해야 할까?

 

저 많은 책 정보를 JSP에 출력해봐야한다. 이걸 쉽게 하기 위해 JSTL을 넣은거니까

<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<title>도서 목록</title>
</head>
<body>
<h3>도서 목록</h3>
<!-- mav.addObject("bookVOList", bookVOList); -->
<%-- <p>${bookVOList}</p> --%>
<table border="1">
	<thead>
		<tr>
			<th>번호</th>
			<th>제목</th>
			<th>카테고리</th>
			<th>가격</th>
			<th>등록일</th>
		</tr>
	</thead>
	<tbody>
		<!-- 
	      forEach 태그? 배열(String[], int[][]), Collection(List, Set) 또는 
	      Map(HashTable, HashMap, SortedMap)에 저장되어 있는 값들을 
	      순차적으로 처리할 때 사용함. 자바의 for, do~while을 대신해서 사용함
	      var : 변수
	      items : 아이템(배열, Collection, Map)
	      varStatus : 루프 정보를 담은 객체 활용
	         - index : 루프 실행 시 현재 인덱스(0부터 시작)
	         - count : 실행 회수(1부터 시작. 보통 행번호 출력)
	       -->
	       <!-- data : mav.addObject("bookVOList", bookVOList); -->
	       <!-- row : bookVO 1행 -->
		<c:forEach var="bookVO" items="${bookVOList}" varStatus="stat">
			<tr>
				<td>${stat.count}</td>
				<td>${bookVO.title}</td>
				<td>${bookVO.category}</td>
				<td>${bookVO.price}</td>
				<td>${bookVO.insertDate}</td>
			</tr>
		</c:forEach>
	</tbody>
</table>
</body>
</html>

 

번호가 127까지 쭉 나온다.

 

근데 이제보니 가격쪽이 좀 불편하다. 천단위 컴마가 안찍혀 나온다.

 

코드수정을 좀 해보자

 

우선 JSTL을 하나 더 추가해준다.

<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

 

그 다음 book.price 부분을 바꿔주도록 하자

<td><fmt:formatNumber value="${bookVO.price}" pattern="#,###" />원</td>

 

이러면 천단위 컴마는 찍히게 된다.

 

도서 등록

a태그

 

도서 등록을 위한 a태그를 만들고 누르면 이전에 만든 create.jsp로 넘어가게 된다.

controller의 createPost 메소드

 

어제 만들어놓은 createPost의 redirect 부분을 /list로 변경해 도서등록이 완료되면 list로 넘어가도록 해주자.

 

새로운 책 정보 입력
list.jsp에 잘 들어간다.

 

SELECT문 SQL에 DESC를 걸어주었기 때문에 역순으로 출력이 되므로 가장 최근에 넣은 정보가 먼저 나온다.

 

이제 제목을 누르면 상세정보가 뜨도록 해보자

 

상세 정보

결국 위 했던 내용대로 하면 된다. controller랑 jsp 매핑하고 Service, Dao 호출해서 연결하고 SQL문 짜고... 의 반복인 것

 

일단 링크부터 연결해주자. jsp에서 title출력부분을 수정해주자

<td><a href="/detail?bookId=${bookVO.bookId}">${bookVO.title}</a></td>

 

그리고 detail.jsp는 create.jsp와 모양이 비슷하니까 복사해서 이름만 바꿔주자

 

//책 상세보기
	   //요청된 URI 주소 : http://localhost/detail?bookId=3
	   //요청파라미터, 쿼리 스트링(Query String) : bookId=3 
	   //요청방식 : get
	   //매개변수 : bookVO => {"bookId":"3","title":"null","category":"null","price":0(null),"insertDate":"null"}
	@RequestMapping(value="/detail")
	public ModelAndView detail(BookVO bookVO, ModelAndView mav) {
		log.info("detail -> bookVO : " + bookVO);
		
		// bookVO = this.bookService.detail(bookVO);
        
		// Model : 데이터
		mav.addObject("title", "도서 상세");
        // mav.addObject("bookVO", bookVO);
		
		// View : jsp
		mav.setViewName("book/detail");
		
		return mav;
	}

 

Controller에 코드를 추가해주고 /detail로 들어가보면

create같지만 detail로 온게 맞다

 

create를 복사해오고 수정을 안했기 때문에 create와 똑같은 페이지가 출력된다.

(제목에 걸려있는 링크를 타고 들어가면 오류가 나니 주의하자)

 

<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>Insert title here</title>
</head>
<body>
<h1>책 등록</h1>
<!-- mav.addObject("title","도서 생성") -->
<h5>${title}</h5>
<p>${bookVO}</p>
<!-- 
요청URI : /crate
요청파라미터 : {title=개똥이의 모험, category=소설, price=12000}
요청방식 : post
-->
<form action="/create" method="post">
	<p>제목 : <input type="text" name="title" required placeholder="타이틀"></p>
	<p>카테고리 : <input type="text" name="category" required placeholder="카테고리"></p>
	<p>가격 : <input type="text" name="price" required placeholder="가격"></p>
	<p>
		<input type="button" id="edit" value="수정">
		<input type="button" id="delete" value="삭제">
		<input type="button" id="list" value="목록">
	</p>
</form>
</body>
</html>

 

이렇게 수정해 준 뒤 버튼마다 각각 이벤트를 달아주자

jQuery를 사용할것이기 때문에 

js.zip
0.79MB
css.zip
0.21MB

 

이 두 파일을 각각 압축을 푼 뒤

경로

위 사진과 같은 경로에 넣어준다(js폴더에 빨간 X표시는 무시해도 된다. 돌아가는데 문제 없음)

 

이제 jsp 상단 스크립트 영역에

<script type="text/javascript" src="/resources/js/jquery.min.js"></script>

 

를 추가해주면 jQuery를 사용할 수 있다.

더보기
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
	<!-- static folder설정(정적 폴더 설정)=>css, images, upload, js 
	   서버에서 앞서 처리될 필요가 없는 정적 리소스 파일을 처리하는 역할 수행
	   웹 애플리케이션의 물리적 경로 이름을 설정하고 이 경로에 정적 리소스 파일들을 저장하면
	   소스 코드나 웹 브라우저의 주소창에서 해당 리소스의 경로를 사용하여 직접 접속할 수 있음
	   정적 리소스란 클라이언트에서 요청이 들어왔을 때 요청 리소스가 이미 만들어져 있어 그대로 응답하는 것
	   mapping : 웹 요청 경로 패턴을 설정. 컨텍스트 경로를 제외한 나머지 부분의 경로와 매핑
	   location : 웹 애플리케이션 내에서 실제 요청 경로의 패턴에 해당하는 자원 위치를 설정. 위치가 여러 곳이면 각 위치를 쉼표로 구분
   -->
	<resources mapping="/resources/**" location="/resources/" />

 

servlet-context.xml

<script type="text/javascript">
// document 내의 모든 요소들이 로딩된 후에 실행
// 1개의 jsp에 1번만 사용할 것
$(function(){
	// id=list인 값을 찾아 클릭할 때 함수가 실행된다
	$('#list').on('click', function(){
		// 해당 경로로 이동
		location.href="/list";
	})
});
</script>

 

detail에 스크립트를 추가하고 목록 버튼을 누르면 /list로 이동된다.

 

이제 controller의 코드에서 주석처리되었던

// bookVO = this.bookService.detail(bookVO);
// mav.addObject("bookVO", bookVO);

의 주석을 풀어주고

 

Service, Dao를 연결해주자

 

 

(...스킵...)

 

 

Dao쪽에서 1개의 정보만을 가져올 것이기 때문에 selectOne으로 만들어줘야 한다.

public BookVO detail(BookVO bookVO) {
		return this.sqlSessionTemplate.selectOne("book.detail", bookVO);
	}

 

이제 xml로 넘어가 쿼리문을 작성하자

<select id="detail" resultType="bookVO" parameterType="bookVO">
   		select book_id, title, category, price, insert_date 
		from book 
		where book_id = ${bookId}
   </select>

 

 

이제 서버를 재가동 하고 list에 있는 제목 링크를 타고 들어가게 되면

bookId가 127번인 책의 detail 페이지

 

book_id가 127번인 책의 detail 페이지에 데이터가 잘 담겨가게 됀다.

 

detail.jsp의 p태그 안에 있는 input의 value값들을 넣어주고 readonly를 넣으면 수정이 불가능하게 만들어준다.

(넣는 김에 class명도 하나씩 넣어주자)

<p>제목 : <input type="text" name="title" value="${bookVO.title}"
		class="formData" required placeholder="타이틀" readonly></p>
<p>카테고리 : <input type="text" name="category" value="${bookVO.category}" 
		class="formData" required placeholder="카테고리" readonly></p>
<p>가격 : <input type="text" name="price" value="${bookVO.price}"
		class="formData" required placeholder="가격" readonly></p>

 

그럼 이제 수정버튼을 누르면 readonly가 지워지면서 수정이 가능하도록 스크립트를 작성해보자.

 

// 수정모드로 전환
// id=edit인 버튼을 찾아 누르면 함수가 실행된다.
$('#edit').on('click', function(){
    // class="formData"의 readonly 속성을 제거한다.
    $('.formData').removeAttr("readonly");
})

 

$('.formData')로 class명이 formData인 요소들을 모두 불러온 뒤 readonly속성을 제거해줬다. 이제 수정이 가능한 상태로 변경된다.

 

그렇다면 수정모드로 전환되었을 때 버튼이 변경되어야 한다.

수정모드로 들어갔을때 수정, 삭제, 목록 버튼이 아닌 확인, 취소 버튼이 뜨도록 해보려고 한다.

 

jsp를 수정해보도록 하자.

<!-- 일반모드 시작 -->
<p id="p1">
    <input type="button" id="edit" value="수정">
    <input type="button" id="delete" value="삭제">
    <input type="button" id="list" value="목록">
</p>
<!-- 일반모드 끝 -->
<!-- 수정모드 시작 -->
<p id="p2" style="displat:none;">
    <input type="button" id="confirm" value="확인">
    <input type="button" id="cancel" value="취소">
</p>
<!-- 수정모드 끝 -->

 

평소에는 p1의 버튼들만 보이다가 수정버튼이 눌리면 p1은 사라지고 p2의 버튼들이 보이도록 만들어보려고 한다 

 

스크립트를 수정해보자

let title = "${bookVO.title}"
let category = "${bookVO.category}"
let price = "${bookVO.price}"
// document 내의 모든 요소들이 로딩된 후에 실행
// 1개의 jsp에 1번만 사용할 것
$(function(){
	
	// id=list인 값을 찾아 클릭할 때 함수가 실행된다
	$('#list').on('click', function(){
		// 해당 경로로 이동
		location.href="/list";
	})
	
	// 수정모드로 전환
	// id=edit인 버튼을 찾아 누르면 함수가 실행된다.
	$('#edit').on('click', function(){
		$('#p1').css("display", "none")
		$('#p2').css("display", "block")
		
		// class="formData"의 readonly 속성을 제거한다.
		$('.formData').removeAttr("readonly");
	})
	
	//취소버튼 클릭
	   $("#cancel").on("click",function(){
	      $("#p1").css("display","block");
	      $("#p2").css("display","none");
	      //readonly 속성 추가
	      $(".formdata").attr("readonly",true);
	      //입력란 초기화
	      //태그로 접근하면 태그만 슨다
	      $("input[name='title']").val(title);      
	      $("input[name='category']").val(category);      
	      $("input[name='price']").val(price);     
	      console.log("title:"+title+",category:"+category+",price"+price);
	   });
	
	
});

 

위 코드로 인해 수정 버튼을 누르면 수정이 가능하고, 취소 버튼을 누를 시 readonly 속성이 다시 부여되고, 원래 데이터가 들어가게 된다.

 

이제 수정버튼을 누른 후 확인을 누르면 수정이 되도록 만들어야 한다.

 

Edit(Update)

우선 jsp를 조금 변경해주자

<form id="frm" name="frm" action="/updatePost" method="post">
	<input type="hidden" name="bookId" value="${bookVO.bookId}">

form에 id와 name값을 넣어주고 action값을 변경해주자

form안에 name속성이 있는 모든 데이터들의 값이 updatePost를 받는 controller로 넘어가게 될 것이다.

 

수정하려면 쿼리문 안에 bookId의 값이 들어있어야 하기 때문에 input type="hidden"으로 보이지 않게 만들어 준 뒤 name값을 부여해 form이 전송될 때 같이 parameter로 넘어가게 된다.

 

//	요청URI : /updatePost
//	요청파라미터 : {bookId=127, title=개똥이의 모험2, category=소설2, price=12002}
//	요청방식 : post
@RequestMapping(value="/updatePost", method=RequestMethod.POST)
public ModelAndView updatePost(BookVO bookVO, ModelAndView mav) {
    log.info("updatePost->bookVO : " + bookVO);

    int result = this.bookService.updateVO(bookVO);

    // redirect -> 새로운 URI를 재요청  /detail?bookId=127
    mav.setViewName("redirect:/detail?bookId=" + bookVO.getBookId());
    return mav;
	}
}

 

컨트롤러에서 받아준다.

insert, update, delete의 반환값은 int값이기 때문에 int result로 선언해주어야 한다.

 

이제 또 다시 Service, Dao, SQL문을 작성하면 된다.

ServiceImpl

 

Dao
SQL문
변경 전
변경 후

 

Delete

아.. 귀찮다...

우선 detail.jsp로 가서 delete버튼을 눌렀을 때 form의 action이 delestPost로 변경되게 해야한다.

스크립트를 수정해보자

// 삭제 버튼 클릭
$('#delete').on('click', function(){
    $('#frm').attr('action','/deletePost');

    let result = confirm("삭제하시겠습니까?")
    // console.log("result : " + result)

    // result : true(확인) / false(취소)
    if(result > 0){ // true
        $('#frm').submit();
        alert("삭제되었습니다.");
    }else{ // false
        alert("삭제가 취소되었습니다.");
        return;
    }
})

 

삭제버튼을 클릭 시 form의 action 속성이 /deletePost로 변경되고, confirm을 통해 한 번 더 확인한다.

확인은 true, 취소는 false를 반환하며

 

if문을 통해 true(확인)일때 submit()을 하여 deletePost에 데이터를 전송하고,

false(취소)일 때 return을 하여 함수를 종료한다.

 

이제 controller에서 데이터를 받아주자

 

@RequestMapping(value="/deletePost", method=RequestMethod.POST)
public ModelAndView deletePost(BookVO bookVO, ModelAndView mav) {
    log.info("deletePost->bookVO : " + bookVO);

    int result = this.bookService.delete(bookVO);
    log.info("deletePost->result : " + result);

    mav.setViewName("redirect:/list");

    return mav;
}

 

아까 말했다싶이 insert, update, delete는 반환값이 int값이기 때문에 int타입으로 선언해준다.

이제 또 반복이다. Service, Dao, SQL.xml을 연결해주고 결과를 실행해보자

 

(...스킵...)

 

삭제버튼 클릭

 

취소버튼을 누르면 아무일도 일어나지 않는다.

 

confirm창에서 확인 버튼을 누를 시
제목 126번의 책이 삭제되었다.

 

잘 삭제되었다.

 

마지막으로 검색기능을 넣어주자

 

search

우선 jsp에 검색어를 입력할 input과 전송할 버튼을 만들어주자

<!-- action속성 및 값이 생략 시, 현재 URI(/list)를 재요청. 
      method는 GET(form 태그의 기본 HTTP 메소드는 GET임) 
	   param : keyword=알탄
	   요청URI : /list?keyword=알탄 or /list or /list?keyword=
	   요청파라미터 : keyword=알탄
	   요청방식 : get
   -->
	<form>
		<input type="text" name="keyword" value="" placeholder="검색어를 입력하세요">
		<button type="button" id="btnButton">검색</button><br>
	</form>

 

도서 등록이 들어있는 p태그 안에 넣어주자

 

이후 스크립트 태그 안에 아래 코드를 작성해주자.

$(function() {
	   $('#btnSearch').on('click',function(){
	      let keyword = $("input[name='keyword']").val();
	      
	      // json오브젝트
	      let data = {
	         "keyword":keyword   
	      };
	      
	      console.log("data :", data);
	      
	      $(this).parent().submit();
	   });
	});

 

input요소 중 name값이 keyword인 요소의 value를 가져와서 data로 JSON화 시킨 후 submit을 시켜준 것.

 

이제 컨트롤러에서 작업을 해야하는데 조금 중요한 내용이 들어있다. 

@RequestMapping(value="/list", method=RequestMethod.GET)
public ModelAndView list(ModelAndView mav, 
        @RequestParam(value="keyword", required=false, defaultValue="") String keyword) {
    log.info("list에서 왔다 : " + mav);
    log.info("list에서 왔다 : " + keyword);

    // map("keyword":"알탄")
    Map<String, Object> map = new HashMap<String, Object>();
    map.put("keyword", keyword);

    List<BookVO> bookVOList = this.bookService.list(map);
    log.info("list->bookVOList : " + bookVOList);

    // Model : 데이터
    mav.addObject("bookVOList", bookVOList);

    // View : jsp
    // forwarding
    mav.setViewName("book/list");

    return mav;
}

 

 

우리가 작성한 list 메소드에 파라미터를 잘 살펴보면 

@RequestParam(value="keyword", required=false, defaultValue="") String keyword)

이러한 어노테이션으로 들어가 있다. 

required=false : 파라미터가 없어도 무관

defaultValue="" : 파라미터가 없을 시 null이 아닌 공백으로 처리

하라는 어노테이션이다. 

 

받아온 keyword값을 map에 저장하게 되면  ("keyword":"알탄") 이런 형식으로 저장될 것이다. (key:value)

이제 list에 파라미터를 map으로 넣어주면 .list가 오류가 날 텐데 

 

두번째 change 어쩌구를 눌러 service 인터페이스의 메소드를 변경하고 나머지 Impl과 Dao도 변경해주자.

 

Service 인터페이스
ServiceImpl
DAO

 

 

SQL 작성하는 xml도 바꿔줘야 한다.

<!-- where 1 = 1은 늘 참임. 
   조건이 2개 이상일 때 WHERE + AND
   조건이 1개일 때 WHERE이어야 함. 
      WHERE(생략) 
      AND   => 오류 발생
      ==>
      WHERE 1 = 1
      AND(생략)
      AND => 정상

      True and True  = True
      True and False = False

      keyword : null(/list)
      keyword : ""  (/list?keyword=) -->
     <select id="list" resultType="bookVO" parameterType="hashMap">
        SELECT BOOK_ID, TITLE, CATEGORY, PRICE, INSERT_DATE
        FROM     BOOK
        WHERE  1 = 1
        <if test="keyword != null and keyword != ''">
            AND    (TITLE	 LIKE '%'|| #{keyword} ||'%' 
                    OR CATEGORY  LIKE '%'|| #{keyword} ||'%'
                    OR PRICE 	 LIKE '%'|| #{keyword} ||'%')
        </if>
        ORDER BY BOOK_ID DESC
        
   </select>

 

parameterType을 추가해줘야 한다. map은 HashMap으로 선언했기 때문에 넣어주지 않으면 오류가 난다.

 

WHERE 1 = 1 조건문을 유연하게 추가하기 위한 절이다.

뒤에 이어지는 동적인 조건을 추가할 때 이 부분에 추가 조건을 쉽게 붙일 수 있다.

 

참고 : https://hyjykelly.tistory.com/5

더보기

WHERE 1 = 1  무조건 True 값이 나오는 문을 WHERE절 앞에 둘 경우 AND 절은 동적으로 이용이 가능해져 Param 값이 NULL일 경우 처리되지 않도록 할 수 있다.

 

MyBatis의 OGNL(Object-Graph Navigation Language) 표현식

<if test=" 조건문 ">

</if>

 

알탄을 검색했을때

 

금액 검색도 가능하다

 

 

3일자 종료!