일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- ddit
- pyqt
- 맥
- ibatis
- spring
- Mac
- jsp
- Error
- 배열
- crud
- 반복문
- 자바
- API
- 객체지향
- nodejs
- python
- 생활코딩
- 컬렉션프레임워크
- FastAPI
- 대덕인재개발원
- 자바문제
- Oracle
- Java
- Android
- Homebrew
- 이클립스
- servlet
- html
- 단축키
- JDBC
- Today
- Total
romworld
Spring 13 - 1:N 관계 테이블(BOOK,LPROD)을 이용하여 상세화면 구현 본문
1:N 관계테이블을 join하여 select 후 상세화면 구현
LPROD 먼저 상세화면 구현해보자
<SQL 상식 상세보기 쿼리문 EQI JOIN,INNER JOIN,>
--http://localhost/lprod/detail?lprodGu=P101
--1) EQUI JOIN
SELECT A.LPROD_ID, A.LPROD_GU, A.LPROD_NM,
B.SEQ, B.FILENAME, B.FILESIZE, B.THUMBNAIL, B.REGDATE, B.ETP_ID
FROM LPROD A, ATTACH B
WHERE A.LPROD_GU = B.ETP_ID
AND A.LPROD_GU = 'P101';
--2) INNER JOIN
SELECT A.LPROD_ID, A.LPROD_GU, A.LPROD_NM,
B.SEQ, B.FILENAME, B.FILESIZE, B.THUMBNAIL, B.REGDATE, B.ETP_ID
FROM LPROD A INNER JOIN ATTACH B
ON(A.LPROD_GU = B.ETP_ID AND A.LPROD_GU = 'P101');
--한 건만 조회해야하므로
/
SELECT A.LPROD_ID, A.LPROD_GU, A.LPROD_NM,
B.SEQ, B.FILENAME, B.FILESIZE, B.THUMBNAIL, B.REGDATE, B.ETP_ID
FROM LPROD A LEFT OUTER JOIN ATTACH B
ON(A.LPROD_GU = B.ETP_ID)
WHERE A.LPROD_GU = 'P102';
/
1 : N
부모 (LPROD) 자식(ATTACH)
부모테이블의 기본키가 자식 테이블의 외래키로 전이됨
자식테이블의 외래키가 부모테이블의 기본키를 참조함
<lprod_SQL.xml> 수정
- resultMap 사용
<!-- LPROD : ATTACH : 1 : N -->
<resultMap type="lprodVO" id="lprodMap">
<result property="lprodGu" column="LPROD_GU"/>
<result property="lprodId" column="LPROD_ID" />
<result property="lprodNm" column="LPROD_NM" />
<collection property="attachVOList" resultMap="attachMap"></collection>
</resultMap>
<resultMap type="attachVO" id="attachMap">
<result property="etpId" column="ETP_ID"/>
<result property="seq" column="SEQ"/>
<result property="filename" column="FILENAME"/>
<result property="filesize" column="FILESIZE"/>
<result property="thumbnail" column="THUMBNAIL"/>
<result property="regdate" column="REGDATE"/>
</resultMap>
- detail 부분도 변경
<!-- 상품분류 상세보기 -->
<select id="detail" parameterType="lprodVO" resultMap="lprodMap">
SELECT A.LPROD_ID, A.LPROD_GU, A.LPROD_NM,
B.SEQ, B.FILENAME, B.FILESIZE, B.THUMBNAIL, B.REGDATE, B.ETP_ID
FROM LPROD A LEFT OUTER JOIN ATTACH B ON(A.LPROD_GU = B.ETP_ID)
WHERE A.LPROD_GU = #{lprodGu}
</select>
<detail.jsp>
부트스트랩 다운로드 받는다.
다운 받은 css,js에서 필요한 파일만 선택해서 resources 에서 각각 넣어준다.
https://getbootstrap.kr/docs/5.2/components/card/
카드
여러 가지 종류와 옵션을 가진 유연하고 확장 가능한 콘텐츠를 제공합니다.
getbootstrap.kr
<수정모드 끝에 삽입> 후 수정
<div class="card" style="width: 100px;">
<img src="..." class="card-img-top" alt="...">
<div class="card-body">
<h5 class="card-title"></h5>
<p class="card-text"></p>
<a href="#" class="btn btn-primary">보기</a>
</div>
</div>
상단에
<link rel="stylesheet" href="/resources/css/bootstrap.min.css" />
<script type="text/javascript" src="/resources/js/bootstrap.min.js"></script>
썸네일을 ATTACH 테이블에 셋팅하기
<SQL 쿼리문> 디벨로퍼에서 실행해준다.
--FILENAME의 데이터를 THUMBNAIL로 만들기
UPDATE ATTACH
SET THUMBNAIL = FILENAME;
COMMIT;
UPDATE ATTACH
SET THUMBNAIL = (SUBSTR(THUMBNAIL,1,INSTR(THUMBNAIL,'/',1,4))
|| 's_'
|| SUBSTR(THUMBNAIL,INSTR(THUMBNAIL,'/',1,4)+1))
<detail.jsp>
- card div를 다시 고치자
<!-- data.attachVOList : List<AttachVO> -->
<c:forEach var="attachVO" items="${data.attachVOList}" varStatus="stat">
<div class="card" style="width: 150px; float:left;">
<img src="/resources/upload/${attachVO.thumbnail}" class="card-img-top"
title="${attachVO.thumbnail}" alt="${attachVO.thumbnail}" />
<div class="card-body">
<h5 class="card-title"></h5>
<p class="card-text">파일크기:${attachVO.filesize}</p>
<a href="#" class="btn btn-primary">보기</a>
</div>
</div>
</c:forEach>
<lprod_SQL.xml>
update 문에 #{attachVO.thumbnail}로 수정해주자
<!-- 다중 insert 시 update 태그를 사용
파라미터 : List<AttachVO attachVOList
insert, update, delete의 경우 resultType은 생략
separator=" " : 공백으로 구분
-->
<update id="createPostAttach" parameterType="java.util.List">
<!--
order="BEFORE"는 foreach 태그 실행 전에 selectKey부터 실행
-->
<selectKey resultType="int" order="BEFORE" keyProperty="seq">
SELECT NVL(MAX(SEQ),0)+1 FROM ATTACH
</selectKey>
<!-- index : 반복 시 index값. 0부터 시작. -->
<foreach collection="list" item="attachVO"
open="INSERT ALL" close="SELECT * FROM DUAL" separator=" "
index="idx" >
INTO ATTACH(SEQ, FILENAME, FILESIZE, THUMBNAIL, REGDATE, ETP_ID)
VALUES(#{idx}+#{seq},#{attachVO.filename},#{attachVO.filesize},#{attachVO.thumbnail},sysdate,
#{attachVO.etpId})
</foreach>
</update>
https://getbootstrap.kr/docs/5.2/components/accordion/
<div class="accordion" id="accordionExample"> <div class="accordion-item"> <h2 class="accordion-header" id="headingOne"> <button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne"> Accordion Item #1 </button> </h2> <div id="collapseOne" class="accordion-collapse collapse show" aria-labelledby="headingOne" data-bs-parent="#accordionExample"> <div class="accordion-body">
아코디언을 적용시켜서 깔끔하게 보여주자
첨부파일을 펼치고 닫기 가능
<detail.jsp>
<!-- 수정모드 끝 -->
<hr />
<div class="accordion" id="accordionExample">
<div class="accordion-item">
<h2 class="accordion-header" id="headingOne">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
첨부파일
</button>
</h2>
<div id="collapseOne" class="accordion-collapse collapse show" aria-labelledby="headingOne" data-bs-parent="#accordionExample">
<div class="accordion-body">
<!-- data.attachVOList : List<AttachVO> -->
<c:forEach var="attachVO" items="${data.attachVOList}" varStatus="stat">
<div class="card" style="width: 150px;">
<img src="/resources/upload/${attachVO.thumbnail}" class="card-img-top"
title="${attachVO.thumbnail}" alt="${attachVO.thumbnail}" />
<div class="card-body">
<h5 class="card-title"></h5>
<p class="card-text">파일크기:${attachVO.filesize}</p>
<a href="#" class="btn btn-primary">보기</a>
</div>
</div>
</c:forEach>
</div>
</div>
</div>
</div>
첨부파일의 보기를 눌렀을 때 모달창 출력하기
1. 첫번째 방법
- 부트스트랩 사용
https://getbootstrap.kr/docs/5.2/components/modal/
모달
Bootstrap JavaScript 모달 플러그인을 사용하여 라이트박스, 사용자 알림 또는 사용자 정의 콘텐츠를 만들 수 있습니다.
getbootstrap.kr
모달창을 복사해놓은다
<!-- Modal -->
<div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="exampleModalLabel">Modal title</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
...
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>
2. a 태그
3. 기타 요소 (p태그)
<button type="button" class="btn btn-primary"
data-toggle="modal" data-target="#modal-default">
Default Modal 띄우기
</button>
<hr />
<a data-toggle="modal" href="#modal-default">Default Modal 띄우기2</a>
<hr />
<p data-toggle="modal" data-target="#modal-default">Default Modal 띄우기3</p>
<div class="accordion" id="accordionExample">
<div class="accordion-item">
<h2 class="accordion-header" id="headingOne">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
첨부파일
</button>
</h2>
<div id="collapseOne" class="accordion-collapse collapse show" aria-labelledby="headingOne" data-bs-parent="#accordionExample">
<div class="accordion-body">
<!-- data.attachVOList : List<AttachVO> -->
<!-- 모달을 띄우는 방법
1. button으로 띄우기
<button type="button" class="btn btn-default"
data-toggle="modal" data-target="#modal-default">
Default Modal 띄우기
2. a 태그로 띄우기
<a data-toggle="modal" href="#modal-default">
3. 기타 요소로 띄우기
<p data-toggle="modal" data-target="#modal-default"></p>
-->
모달창 닫는 스크립트 추가
<script>
//모달 닫기
$("#modalClose").on("click", function(){
$("#modal-default").modal("hide");
});
</script>
<detail.jsp>
<%@ page language="java" contentType="text/html;charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<link rel="stylesheet" href="/resources/css/bootstrap.min.css" />
<script type="text/javascript" src="/resources/js/bootstrap.min.js"></script>
<script type="text/javascript" src="/resources/js/jquery-3.6.0.js"></script>
<script type="text/javascript">
//document의 요소들이 모두 로딩된 후 실행
$(function(){
$(".btn-icon-split").on("click",function(){
console.log("개똥이");
//상품분류코드 자동생성
//아작났어유..피씨다타써
/*
url : 요청경로
contentType:보내는 데이터의 타입
dataType:응답 데이터 타입
data : 요청 시 전송할 데이터
type : 요청방식. get, post, put
*/
$.ajax({
url:"/lprod/getLprodGu",
type:"post",
success:function(result){
console.log("result : " + result);
$("input[name='lprodGu']").val(result);
}
});
});
$("#btnRegist").on("click",function(){
let lprodGu = $("#lprodGu").val();
let lprodNm = $("#lprodNm").val();
if(jQuery.trim(lprodGu)==""){
alert("상품분류코드를 작성해주세요");
$("#lprodGu").focus();
return false;
}
if(jQuery.trim(lprodNm)==""){
alert("상품분류명을 작성해주세요");
$("#lprodNm").focus();
return false;
}
//[submit]을 실행
//달러("#frm").submit(function(){alert("등록이 진행됩니다.");});
/*
요청URI : /lprod/createPost
요청파라미터 : {"lprodId":"10","lprodGu":"P404","lprodNm":"간식류"}
요청방식 : post
*/
$("form").submit();
});
//수정버튼 클릭 -> 수정모드로 전환. <button type="button" id="edit"
$("#edit").on("click",function(){
//모드 변경
$("#spn1").css("display","none");
$("#spn2").css("display","block");
//1) 분류코드 자동생성 버튼 활성화
//$(".btn-icon-split").removeAttr("disabled"); 있다는 것만 알아두자 굳이 활성화 할 필요 없당
//2) 입력란 활성화
$(".form-control-user").removeAttr("readonly");
//3) 상품분류 아이디/ 상품분류코드 비활성화
$("#lprodId").attr("readonly","true");
$("#lprodGu").attr("readonly","true");
//4) form의 action속성의 속성값을 updatePost로 변경
$("#frm").attr("action","/lprod/updatePost");
});
//삭제버튼 클릭
$("#delete").on("click", function(){
// form의 action속성의 속성값을 /lprod/deletePost로 변경
$("#frm").attr("action","/lprod/deletePost");
// 삭제여부 재확인
// true(1) / false(0)
let result = confirm("삭제하시겠습니까?");
// submit
if(result>0){ // 삭제 실행
$("#frm").submit();
}else{//삭제 안함
alert("삭제가 취소되었습니다.")
}
});
});
</script>
<div class="container">
<div class="card o-hidden border-0 shadow-lg my-5">
<div class="card-body p-0">
<!-- Nested Row within Card Body -->
<div class="row">
<div class="col-lg-5 d-none d-lg-block bg-register-image"></div>
<div class="col-lg-7">
<div class="p-5">
<div class="text-center">
<h1 class="h4 text-gray-900 mb-4">Create an Account!</h1>
</div>
<form id="frm" class="user" action="/lprod/createPost"
method="post">
<div class="form-group row">
<div class="col-sm-6 mb-3 mb-sm-0">
<!-- 변경 제약
disabled : 값이 전달 안됨
readonly : 파라미터로 값이 전달됨
-->
<input type="text" class="form-control form-control-user"
id="lprodId" name="lprodId"
value="${data.lprodId}"
placeholder="상품분류 아이디"
readonly>
</div>
<div class="col-sm-6">
<button type="button" class="btn btn-info btn-icon-split"
disabled>
<span class="icon text-white-50">
<i class="fas fa-info-circle"></i>
</span>
<span class="text">분류코드 자동생성</span>
</button>
</div>
</div>
<div class="form-group">
<input type="text" class="form-control form-control-user"
id="lprodGu" name="lprodGu" placeholder="상품분류 코드"
value="${data.lprodGu}" required readonly />
</div>
<div class="form-group">
<input type="text" class="form-control form-control-user"
id="lprodNm" name="lprodNm" placeholder="상품분류 명"
value="${data.lprodNm}" required readonly />
</div>
<!-- 일반모드 시작 -->
<span id="spn1">
<p>
<button type="button" id="edit"
class="btn btn-warning btn-user btn-block"
style="width:50%;float:left;">
수정
</button>
<button type="button" id="delete"
class="btn btn-danger btn-user btn-block"
style="width:50%;">
삭제
</button>
</p>
</span>
<!-- 일반모드 끝 -->
<!-- 수정모드 시작 -->
<span id="spn2" style="display:none;">
<p>
<button type="submit"
class="btn btn-warning btn-user btn-block"
style="width:50%;float:left;">
확인
</button>
<!-- lprodGu=P404 : 달라{param} -->
<button type="button"
onclick="javascript:location.href='/lprod/detail?lprodGu=${param.lprodGu}'"
class="btn btn-danger btn-user btn-block"
style="width:50%;">
취소
</button>
</p>
</span>
<!-- 수정모드 끝 -->
<hr />
<button type="button" class="btn btn-primary"
data-toggle="modal" data-target="#modal-default">
Default Modal 띄우기
</button>
<hr />
<a data-toggle="modal" href="#modal-default">Default Modal 띄우기2</a>
<hr />
<p data-toggle="modal" data-target="#modal-default">Default Modal 띄우기3</p>
<div class="accordion" id="accordionExample">
<div class="accordion-item">
<h2 class="accordion-header" id="headingOne">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
첨부파일
</button>
</h2>
<div id="collapseOne" class="accordion-collapse collapse show" aria-labelledby="headingOne" data-bs-parent="#accordionExample">
<div class="accordion-body">
<!-- data.attachVOList : List<AttachVO> -->
<!-- 모달을 띄우는 방법
1. button으로 띄우기
<button type="button" class="btn btn-default"
data-toggle="modal" data-target="#modal-default">
Default Modal 띄우기
2. a 태그로 띄우기
<a data-toggle="modal" href="#modal-default">
3. 기타 요소로 띄우기
<p data-toggle="modal" data-target="#modal-default"></p>
-->
<c:forEach var="attachVO" items="${data.attachVOList}" varStatus="stat">
<div class="card" style="width: 150px;">
<img src="/resources/upload/${attachVO.thumbnail}" class="card-img-top"
title="${attachVO.thumbnail}" alt="${attachVO.thumbnail}" />
<div class="card-body">
<h5 class="card-title"></h5>
<p class="card-text">파일크기:${attachVO.filesize}</p>
<a href="#" class="btn btn-primary">보기</a>
</div>
</div>
</c:forEach>
</div>
</div>
</div>
</div>
<a href="/lprod/list" class="btn btn-primary btn-user btn-block">
<i class="fab fa-google fa-fw"></i> 목록보기
</a>
</form>
<hr>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Modal -->
<div class="modal fade" id="modal-default" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="exampleModalLabel">Modal title</h1>
<button type="button" class="btn-close" data-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
...
</div>
<div class="modal-footer">
<button type="button" id="modalClose" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<script>
//모달 닫기
$("#modalClose").on("click", function(){
$("#modal-default").modal("hide");
});
</script>
이제 연습으로 넣은 세가지 방법은 주석 처리하고
이미지 목록의 보기를 눌렀을 때 모달창이 띄워지도록 하자.
<detail.jsp>
<!-- data.attachVOList : List<AttachVO> -->
<!-- 모달을 띄우는 방법
1. button으로 띄우기
<button type="button" class="btn btn-default"
data-toggle="modal" data-target="#modal-default">
Default Modal 띄우기
2. a 태그로 띄우기
<a data-toggle="modal" href="#modal-default">
3. 기타 요소로 띄우기
<p data-toggle="modal" data-target="#modal-default"></p>
-->
<c:forEach var="attachVO" items="${data.attachVOList}" varStatus="stat">
<div class="card" style="width: 150px; display:inline-block;">
<img src="/resources/upload/${attachVO.thumbnail}" class="card-img-top"
title="${attachVO.thumbnail}" alt="${attachVO.thumbnail}" />
<div class="card-body">
<h5 class="card-title"></h5>
<p class="card-text">파일크기:${attachVO.filesize}</p>
<a data-toggle="modal" href="#modal-default"
data-filename="${attachVO.thumbnail}"
class="btn btn-outline-primary ">보기</a>
</div>
</div>
</c:forEach>
</div>
</div>
</div>
</div>
하단에 모달, 스크립트 작성
<!-- Modal -->
<div class="modal fade" id="modal-default" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="exampleModalLabel">첨부파일</h1>
<button type="button" class="btn-close" data-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body"></div>
<div class="modal-footer">
<button type="button" id="modalClose" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<script type="text/javascript">
$(function(){
//모달 닫기
$("#modalClose").on("click", function(){
$("#modal-default").modal("hide");
});
// 이미지 목록의 보기를 클릭 시 정보들을 모달창으로 보냄
$(".btn-outline-primary").on("click", function(){
//달러(this) : 클래스로 요소들을 선택했으므로
let filename = $(this).data("filename");
console.log("filename : " + filename);
//modal-body 클래스인 요소에 이미지를 보이자(html은 새로고침, append는 누적)
//<div class="modal-body"></div>
$(".modal-body").html("<img src='/resources/upload" + filename +
"' style='width:100%' />")
});
})
</script>
book도 똑같이 적용시켜보자
<book_SQL.xml>
- detail 쿼리문 left join으로 수정
- resultMap 추가
<!-- BOOK : ATTACH : 1 : N -->
<resultMap type="bookVO" id="bookMap">
<result property="bookId" column="BOOK_ID"/>
<result property="title" column="TITLE" />
<result property="category" column="CATEGORY" />
<result property="price" column="PRICE" />
<result property="insertDate" column="INSERT_DATE" />
<result property="content" column="CONTENT" />
<collection property="attachVOList" resultMap="attachMap"></collection>
</resultMap>
<resultMap type="attachVO" id="attachMap">
<result property="etpId" column="ETP_ID"/>
<result property="seq" column="SEQ"/>
<result property="filename" column="FILENAME"/>
<result property="filesize" column="FILESIZE"/>
<result property="thumbnail" column="THUMBNAIL"/>
<result property="regdate" column="REGDATE"/>
</resultMap>
<select id="detail" parameterType="bookVO" resultMap="bookMap">
SELECT A.BOOK_ID, A.TITLE, A.CATEGORY, A.PRICE, A.INSERT_DATE, A.CONTENT,
B.SEQ, B.FILENAME, B.FILESIZE, B.THUMBNAIL, B.REGDATE, B.ETP_ID
FROM BOOK A LEFT JOIN ATTACH B on(TO_CHAR(A.BOOK_ID) = B.ETP_ID)
WHERE A.BOOK_ID = #{bookId}
</select>
<?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">
<!-- namespace : xml 파일이 여러개일 수 있으므로
이를 구별하기 위한 식별 용도로 사용 -->
<mapper namespace="book">
<!-- BOOK : ATTACH : 1 : N -->
<resultMap type="bookVO" id="bookMap">
<result property="bookId" column="BOOK_ID"/>
<result property="title" column="TITLE" />
<result property="category" column="CATEGORY" />
<result property="price" column="PRICE" />
<result property="insertDate" column="INSERT_DATE" />
<result property="content" column="CONTENT" />
<collection property="attachVOList" resultMap="attachMap"></collection>
</resultMap>
<resultMap type="attachVO" id="attachMap">
<result property="etpId" column="ETP_ID"/>
<result property="seq" column="SEQ"/>
<result property="filename" column="FILENAME"/>
<result property="filesize" column="FILESIZE"/>
<result property="thumbnail" column="THUMBNAIL"/>
<result property="regdate" column="REGDATE"/>
</resultMap>
<!-- MyBatis에서 제공해주는 데이터 입력을 나타내는 태그
1) 드루와 : Dao 객체가 던진 데이터타입. parameterType만 씀
2) 가즈아 : Dao 객체의 메소드 쪽으로 리턴할 타입. insert/update/delete의 경우 생략(당연히 int)
- resultType : String, int, hashMap, VO
- resultMap : 1:N의 관계 . MyBatis의 resultMap 태그를 사용
-->
<!--
(전) bookVO{bookId:0,title:롬이야기,category:소설price:10000,insertDate:null
content:null}
(후) bookVO{bookId:1,title:롬이야기,category:소설price:10000,insertDate:null
content:null}
MyBatis 쿼리 XML에 전달되면 #{멤버변수}로 작성하여 값을 치환
-->
<insert id="createPost" parameterType="bookVO">
<!-- 키를 높이면 락(rOK)커가 될 수 있을까? -->
<selectKey resultType="int" order="BEFORE" keyProperty="bookId">
SELECT NVL(MAX(BOOK_ID),0)+1 FROM BOOK
</selectKey>
INSERT INTO BOOK(BOOK_ID, TITLE, CATEGORY, PRICE, INSERT_DATE, CONTENT)
VALUES(#{bookId},#{title},#{category},#{price},SYSDATE,#{cont})
</insert>
<!-- p.71 -->
<!-- 책 상세보기
select 태그는 조회(select) 쿼리를 실행하기 위한 mybatis 태그
드루와 : parameterType(book_SQL.xml로 드루와) - bookVO 타입
가즈아 : resultType(BookDao로 가즈아) - bookVO 타입
(전) bookVO{bookId:2,title:롬이야기,category:소설price:10000,insertDate:null
content:null}
(후) bookVO{bookId:2,title:롬이야기,category:소설price:10000,insertDate:23/01/20
content:롬이의 모험 이야기}
-->
<select id="detail" parameterType="bookVO" resultMap="bookMap">
SELECT A.BOOK_ID, A.TITLE, A.CATEGORY, A.PRICE, A.INSERT_DATE, A.CONTENT,
B.SEQ, B.FILENAME, B.FILESIZE, B.THUMBNAIL, B.REGDATE, B.ETP_ID
FROM BOOK A LEFT JOIN ATTACH B on(TO_CHAR(A.BOOK_ID) = B.ETP_ID)
WHERE A.BOOK_ID = #{bookId}
</select>
<!-- 책 수정하기. update 태그는 update 쿼리를 실행하기 위한 mybatis 태그 -->
<!-- insert/update/delete의 경우 resultType 생략 가능(당연히 int) -->
<update id="updatePost" parameterType="bookVO">
UPDATE BOOK
SET TITLE=#{title}, CATEGORY=#{category}
,PRICE=#{price},INSERT_DATE=SYSDATE,CONTENT=#{content}
WHERE BOOK_ID =#{bookId}
</update>
<!-- parameterType : int,String,bookVO,hashMap -->
<delete id="deletePost" parameterType="bookVO">
DELETE FROM BOOK
WHERE BOOK_ID IN(#{bookId})
</delete>
<!--
책 목록
parameterType : String keyword
WHERE 1 = 1은 늘 참(true)임
조건이 2개 이상일 때 where + and
조건이 1개일 때 생략 + where이어야 함
=> WHERE 1 = 1로 개선
조건이 2개 이상일 때 and + and
조건이 1개일 때 생략 + and이어야함
-->
<select id="list" parameterType="String" resultType="bookVO">
WITH T AS(
SELECT ROW_NUMBER() OVER(ORDER BY BOOK_ID DESC) RNUM
, BOOK_ID, TITLE, CATEGORY, PRICE, INSERT_DATE, CONTENT
FROM BOOK
WHERE 1 = 1
<if test="keyword!=null and keyword!=''">
AND(
TITLE LIKE '%' || #{keyword} || '%'
OR CATEGORY LIKE '%' || #{keyword} || '%'
OR CONTENT LIKE '%' || #{keyword} || '%'
)
</if>
)
SELECT T.RNUM, T.BOOK_ID, T.TITLE, T.CATEGORY, T.PRICE
, T.INSERT_DATE, T.CONTENT
FROM T
</select>
<!-- 다중 insert 시 update 태그를 사용
파라미터 : List<AttachVO attachVOList
insert, update, delete의 경우 resultType은 생략
separator=" " : 공백으로 구분
-->
<update id="createPostAttach" parameterType="java.util.List">
<!--
order="BEFORE"는 foreach 태그 실행 전에 selectKey부터 실행
-->
<selectKey resultType="int" order="BEFORE" keyProperty="seq">
SELECT NVL(MAX(SEQ),0)+1 FROM ATTACH
</selectKey>
<!-- index : 반복 시 index값. 0부터 시작. -->
<foreach collection="list" item="attachVO"
open="INSERT ALL" close="SELECT * FROM DUAL" separator=" "
index="idx" >
INTO ATTACH(SEQ, FILENAME, FILESIZE, THUMBNAIL, REGDATE, ETP_ID)
VALUES(#{idx}+#{seq},#{attachVO.filename},#{attachVO.filesize},#{attachVO.thumbnail},sysdate,
#{attachVO.etpId})
</foreach>
</update>
</mapper>
<detail.jsp>
- 파일을 꺼내준다.
<!-- 첨부파일 펼치기/닫기 -->
<div class="accordion" id="accordionExample">
<div class="accordion-item">
<h2 class="accordion-header" id="headingOne">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
첨부파일
</button>
</h2>
<div id="collapseOne" class="accordion-collapse collapse show" aria-labelledby="headingOne" data-bs-parent="#accordionExample">
<div class="accordion-body">
<!-- data.attachVOList : List<AttachVO> -->
<!-- 모달을 띄우는 방법 -->
<c:forEach var="attachVO" items="${data.attachVOList}" varStatus="stat">
<div class="card" style="width: 150px; display:inline-block;">
<img src="/resources/upload/${attachVO.thumbnail}" class="card-img-top"
title="${attachVO.thumbnail}" alt="${attachVO.thumbnail}" />
<div class="card-body">
<h5 class="card-title"></h5>
<p class="card-text">파일크기:${attachVO.filesize}</p>
<a data-toggle="modal" href="#modal-default"
data-filename="${attachVO.thumbnail}"
class="btn btn-outline-primary ">보기</a>
</div>
</div>
</c:forEach>
<%@ page language="java" contentType="text/html;charset=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>
<link rel="stylesheet" href="/resources/css/bootstrap.min.css" />
<script type="text/javascript" src="/resources/js/bootstrap.min.js"></script>
<script type="text/javascript" src="/resources/js/jquery.3.6.0.js"></script>
<title>도서관리시스템</title>
<script type="text/javascript" src="/resources/ckeditor/ckeditor.js"></script>
</head>
<body>
<!-- mav.addObject("data", data); => bookVO 데이터 -->
<%-- ${data} --%>
<!-- <hr /> -->
<!-- mav.addObject("bookId", data.getBookId());기본키 값(int 타입) -->
<%-- ${bookId} --%>
<!--
JSTL(JSP Standard Tag Library) : 개발자가 자주 사용하는 패턴을 모아놓은 집합
=> BookController에서 보내준 데이터를 뷰에 표현하도록 도와줌
JSTL은 maven에서 설정되어 있음 => pom.xml => jstl
-->
<h1>책 상세</h1>
<p>제목 : ${data.title} </p>
<p>카테고리 : ${data.category}</p>
<p>가격 : <fmt:formatNumber type="number" pattern="###,###" maxFractionDigits="0"
value="${data.price}"/></p>
<p>입력일 : <fmt:formatDate pattern="yyyy.MM.dd HH:mm:ss"
value="${data.insertDate}" /></p>
<p>내용 :
<textarea name="cont" rows="5" cols="30" readonly>${data.content}</textarea>
</p>
<p><a href="/update?bookId=${bookId}">수정폼</a></p>
<p><a href="/list">목록</a></p>
<p>
<!--
method
1)GET : 데이터를 변경하지 않을 때. 목록/상세보기
2)POST : 데이터를 변경할 때 . 입력/수정/삭제
--1) 단일행 : =,<,>,<=,>=,!=,<>
--2) 다중행 : IN(교집합),ANY(OR연산자),ALL(AND연산자),EXISTS(교집합)
-->
<form action="/delete" method="post">
<!-- 폼 데이터 -->
<input type="hidden" name="bookId" value="${bookId}" />
<button type="submit">삭제</button>
</form>
</p>
<!-- 첨부파일 펼치기/닫기 -->
<div class="accordion" id="accordionExample">
<div class="accordion-item">
<h2 class="accordion-header" id="headingOne">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
첨부파일
</button>
</h2>
<div id="collapseOne" class="accordion-collapse collapse show" aria-labelledby="headingOne" data-bs-parent="#accordionExample">
<div class="accordion-body">
<!-- data.attachVOList : List<AttachVO> -->
<!-- 모달을 띄우는 방법 -->
<c:forEach var="attachVO" items="${data.attachVOList}" varStatus="stat">
<div class="card" style="width: 150px; display:inline-block;">
<img src="/resources/upload/${attachVO.thumbnail}" class="card-img-top"
title="${attachVO.thumbnail}" alt="${attachVO.thumbnail}" />
<div class="card-body">
<h5 class="card-title"></h5>
<p class="card-text">파일크기:${attachVO.filesize}</p>
<a data-toggle="modal" href="#modal-default"
data-filename="${attachVO.thumbnail}"
class="btn btn-outline-primary ">보기</a>
</div>
</div>
</c:forEach>
<script type="text/javascript">
CKEDITOR.replace("cont");
// CKEDITOR.instances['content'].setReadOnly(false);
</script>
</body>
</html>
'Spring' 카테고리의 다른 글
Spring 15 - 파일 다운로드 , Ajax 연습 (0) | 2023.02.02 |
---|---|
Spring 14 - 페이징 처리, 검색 기능, 계층형 쿼리, LOOP을 이용한 UPDATE 쿼리문 (1) | 2023.02.01 |
Spring 12 - Book 테이블도 파일업로드 해보자 (0) | 2023.01.30 |
Spring 11 - LPROD, ATTACH 테이블 연결 (일대다 관계 ResultMap 사용) + 한글깨짐 해결하기 (0) | 2023.01.30 |
Spring 10 - LPROD를 이용한 파일 업로드, 썸네일 (0) | 2023.01.27 |