Spring 18 - Mapper 인터페이스, 스프링 폼 태그 라이브러리, 계층형 쿼리
1. 컨트롤러 요청 처리
2. 자바빈즈 , Data타입
3. Ajax, (Json)
4. 매퍼인터페이스 활용
5. 계층형Query 사용
MyBatis - Mapper Interface
Mapper 인터페이스란?
- 매핑파일에 기재된 SQL을 호출하기 위한 인터페이스이다.
- Mybatis 3.0부터 생겼다.
- 매핑파일에 있는 SQL을 인터페이스로 호출한다.
Mapper 사용하지않았을시?
- session.selectOne("namespace.아이디", 파라미터); 형식 이였다.
- 네임스페이스+"."+SQL ID 로 지정해야한다.
- 문자열로 작성하기때문에 버그가 생길 수 있다.
- IDE에서 제공하는 code assist를 사용할 수 없다.
Mapper 사용했을때
- Mapper 인터페이스 개발자가 직접작성한다.
- 패키지 이름+"."+인터페이스이름+"."+메서드이름이 네임스페이스+"."+SQL의 ID를 설정해야 한다.
- 네임스페이스 속성에는 패키지를 포함한 Mapper 인터페이스 이름 형식이다.
- SQL ID 에는 매핑하는 메서드 이름을 지정하는 것.
Mapper 인터페이스 작성
반드시 인터페이스로 선언해주어야한다.
네임스페이스 명은 패키지포함 인터페이스이름으로 작성.
ex)<mapper namespace="myspring.user.dao.UserMapper">
메서드명은 SQLID와 동일하게작성.
스프링 폼 태그 라이브러리
- 스프링은 HTML 폼을 표시하기 위한 태그 라이브러리
- 스프링 폼을 이용하면 HTML 폼과 자바 객체를 쉽게 바인딩 할 수 있다.
<form:form> | 폼 요소를 생성 |
<form:input> | 텍스트 필드 요소를 생성 |
<form:password> | 패스워드 필드 요소를 생성 |
<form:textarea> | 텍스트 영역 요소 생성 |
<form:checkboxes> | 여러 개의 체크박스 요소를 생성 |
<form:checkbox> | 체크박스 요소를 생성 |
<form:radiobuttons> | 여러 개의 라디오 버튼 요소를 생성 |
<form:radiobutton> | 라디오 버튼 요소 생성 |
<form:select> | 셀렉트 박스 요소를 생성 |
<form:hidden> | 숨겨진 필드 요소를 생성 |
<form:label> | 라벨 요소를 생성 |
<form:button> | 버튼 요소를 생성 |
<form:errors> | 입력값 검증 오류 표시 |
- 스프링 폼 태그 라이브러리 선언
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<CusMapper.java>
- 인터페이스로 생성한다.
package kr.or.ddit.mapper;
public interface CusMapper {
// 고객테이블(CUS)의 기본키 데이터 생성
// <select id="getCusNum" resultType="String">
public String getCusNum();
}
<root-context.xml>
<!-- Mapper 인터페이스 설정
개발자가 직접 DAO를 설정하지 않아도
자동으로 Mapper 인터페이스를 활용하는 객체를 생성하게 됨
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="kr.or.ddit.mapper"></property>
</bean>
<cus_SQL.xml>
<?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="kr.or.ddit.mapper.CusMapper">
<!-- 고객테이블(CUS)의 기본키 데이터 생성 -->
<select id="getCusNum" resultType="String">
SELECT NVL(SUBSTR(MAX(CUS_NUM),1,3),'CUS')
|| TRIM(TO_CHAR(NVL(SUBSTR(MAX(CUS_NUM),4),0)+1,'000'))
FROM CUS
</select>
</mapper>
<CusServiceImpl.java>
package kr.or.ddit.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import kr.or.ddit.mapper.CusMapper;
import kr.or.ddit.service.CusService;
@Service
public class CusServiceImpl implements CusService {
@Autowired
CusMapper cusMapper;
// 고객테이블(CUS)의 기본키 데이터 생성
@Override
public String getCusNum() {
return this.cusMapper.getCusNum();
}
}
<CusService.java>
package kr.or.ddit.service;
public interface CusService {
// 고객테이블(CUS)의 기본키 데이터 생성
public String getCusNum();
}
<cusController.java>
- create 매서드 수정
- cusService autowired 해준다.
- detail 매서드 생성
- createPost 매서드 수정 (요청 파라미터를 String 매개변수로 받을 수 있다)
package kr.or.ddit.controller;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import kr.or.ddit.service.CusService;
import kr.or.ddit.vo.CusVO;
import lombok.extern.slf4j.Slf4j;
// Controller -> 자바빈으로 등록해서 관리
// 속성이 1개일 땐 속성명 생략 가능 (value= << 이 속성이 생략되어있음)
@Slf4j
@RequestMapping("/cus")
@Controller
public class CusController {
@Autowired
CusService cusService;
// void로 응답
// 요청URI : /cus/create
@GetMapping("/create")
public void create(@ModelAttribute CusVO cusVO) {
log.info("create()에 왔다");
//기본키 데이터 생성
String cusNum = this.cusService.getCusNum();
//cusVO{cusNum=CUS002, cusNm=null,cusAddr=null...}
cusVO.setCusNum(cusNum);
//forwarding
//return "cus/create"
}
// String으로 응답*
/*
요청URI : /cus/createPost
요청파라미터 : {cusNum=1234, cusNm=새롬이, postno=33233...}
요청방식 : post
*/
// String, int, Map.. => @RequestParam
// VO => ModelAttribute
// @Valid => CusVO의 validation 체크를 해주는 애너테이션
// 문제발생 시 Errors errors객체에 오류 정보를 담아서 꼭!!! forwarding 해주면 됨
@PostMapping("/createPost")
public String createPost(@Valid @ModelAttribute CusVO cusVO,
Errors errors) {
// errors.hasErrors() : 문제 발생 시 true
if(errors.hasErrors()) {
return "cus/create";
}
// 고객번호는 CUS001, CUS002...
// 자동으로 다음 고객번호를 생성시켜보자
String cusNum = cusService.getCusNum();
cusVO.setCusNum(cusNum);
// 입력성공 : 상세보기로 redirect. 기본키 데이터를 파라미터로 보냄
return "redirect:/cus/detail?cusNum="+cusVO.getCusNum();
}
/* 컨트롤러 매서드 매개변수
- String
- Model
- RedirectAttributes
- 자바빈즈 클래스
- MultipartFile
- BindingResult
- java.util.Locale
- java.security.Principal
*/
//요청URI : /cus/detail
@GetMapping("/detail")
public String detail( Model model, RedirectAttributes ras,
CusVO cusVO) {
//forwarding
return "cus/detail";
}
}
***
여기서 매개변수명을 바꿔서 쓰고 싶으면 RequestParam에서 name을 매핑하고 사용할 변수명을 작성하면
바꿔서 사용할 수 있다
다만 , 바꾸면 헷갈릴 수 있으니 name을 동일하게 해주는 게 낫다
<create.jsp>
- <form:input에는 type="text"랑 id="cusNum", name="cusNum", value가 내장되어서 path에 설정해줌
- 폼데이터 수정
- input type="date"
- 나머지도 고쳐줌
- form:checkbox
<%@ page language="java" contentType="text/html;charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<link rel="stylesheet" href="/resources/css/bootstrap.min.css" />
<script src="https://ssl.daumcdn.net/dmaps/map_js_init/postcode.v2.js"></script>
<script type="text/javascript" src="/resources/js/jquery-3.6.0.js"></script>
<script type="text/javascript">
$(function(){
// 다음 우편번호 검색
$("#btnPostno").on("click", function(){
new daum.Postcode({
// 다음 창에서 검색이 완료되면 콜백함수에 의해 결과 데이터가 data 객체로 들어옴
oncomplete:function(data){
// 우편번호
$("#postno").val(data.zonecode);
// 주소
$("#cusAddr").val(data.address);
// 상세 주소
$("#addrDet").val(data.buildingName);
}
}).open();
});
}) ;
</script>
<!-- ctrl + shift + F : 인덴트 자동 처리 -->
<nav class="navbar bg-body-tertiary">
<div class="container-fluid">
<a class="navbar-brand" href="#">고객관리</a>
</div>
</nav>
<!-- 폼페이지
요청URI : /cus/createPost
요청파라미터 : {cusNum=1234, cusNm=새롬이, postno=33233...}
요청방식 : post
- modelAttribute="cusVO" => 컨트롤러에서 매게변수에 담아줬던 데이터를 연결해주는 것
꼭 이름을 같게 설정할 것
-->
<form:form modelAttribute="cusVO" action="/cus/createPost" method="post">
<!-- 폼데이터 -->
<div class="bd-example">
<div class="mb-3">
<label for="cusNum" class="form-label">고객번호</label>
<!--꺽음쇄form:input에는 type="text"랑 id="cusNum", name="cusNum", value가 내장 -->
<form:input class="form-control"
path="cusNum" placeholder="고객번호를 입력해주세요" />
<!-- path : cusVO객체의 cusNum 멤버변수 -->
<form:errors path="cusNum" style="color:red;" />
</div>
<div class="mb-3">
<label for="cusNm" class="form-label">고객명</label>
<form:input class="form-control"
path="cusNm" placeholder="고객명을 입력해주세요" />
<form:errors path="cusNm" style="color:red;" />
</div>
<div class="mb-3">
<div>
<label for="postno" class="form-label">우편번호</label>
</div>
<div>
<input type="text" class="form-control"
id="postno" name="postno" placeholder="우편번호를 입력해주세요"
style="width:80%;float:left;" />
<input class="btn btn-info" type="button" value="우편번호 검색"
style="width:20%;float:right;" id="btnPostno" />
</div>
</div>
<div class="mb-3">
<label for="cusAddr" class="form-label">주소</label>
<input type="text" class="form-control"
id="cusAddr" name="cusAddr" placeholder="주소를 입력해주세요" />
</div>
<div class="mb-3">
<label for="addrDet" class="form-label">상세 주소</label>
<input type="text" class="form-control"
id="addrDet" name="addrDet" placeholder="상세 주소를 입력해주세요" />
</div>
<div class="mb-3">
<label for="cusPhe" class="form-label">연락처</label>
<input type="text" class="form-control"
id="cusPhe" name="cusPhe" placeholder="연락처를 입력해주세요" />
<form:errors path="cusPhe" style="color:red;" />
</div>
<div class="mb-3">
<label for="cusBir" class="form-label">생일</label>
<input type="date" class="form-control"
id="cusBir" name="cusBir" placeholder="생일을 입력해주세요" />
<form:errors path="cusBir" style="color:red;" />
</div>
<div class="mb-3">
<label for="hobbyList" class="form-label">취미</label>
<form:checkbox path="hobbyList" value="Music" label="Music"/>
<form:checkbox path="hobbyList" value="Movie" label="Movie"/>
<form:checkbox path="hobbyList" value="Sports" label="Sports"/>
</div>
<div>
<button type="submit" class="btn btn-primary btn-lg">등록</button>
</div>
</div>
</form:form>
<CusVO.java>
- private Date cusBir
- private List<CarVO>carVOList;
- private List<String>hobbyList;
package kr.or.ddit.vo;
import java.util.Date;
import java.util.List;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.NotBlank;
import org.springframework.format.annotation.DateTimeFormat;
import lombok.Data;
//고객
/* Bean Validation이 제공하는 제약 애너테이션
- NotNull : 빈 값 체크
- *NotBlank : null 체크, trim(공백제거) 후 길이가 0인지 체크
- Size : 글자 수 체크
- Email : 이메일 주소 형식 체크
- Past : 오늘보다 과거 날짜(ex. 생일)
- Future : 미래 날짜 체크(ex. 예약일)
*/
@Data
public class CusVO {
//고객번호(필수=mandatory), null 체크, trim 후 길이가 0인지 체크
@NotBlank
private String cusNum;
//고객명
@NotBlank
@Size(min=2,max=10,message="2~10자 이내로 입력해 주세요")
private String cusNm;
//주소
private String cusAddr;
//연락처
@NotBlank
private String cusPhe;
//우편번호
private String postno;
//주소 상세
private String addrDet;
//생일
/*
2023-02-06(x) <== input type="date"
20230206(x)
--- 위에 두개는 DateTimeFormat을 써줘야 가능하다.
2023/02/06(o)(기본형식이라 DateTimeFormat이 없어도 됨)
*/
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date cusBir;
//취미
private List<String> hobbyList;
//고객(CUS) : 자동차(CAR) = 1 : N
private List<CarVO> carVOList;
//고객(CUS) : 서비스(SER) = 1 : N
private List<SerVO> serVOList;
}
<CusController.java>
package kr.or.ddit.controller;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import kr.or.ddit.service.CusService;
import kr.or.ddit.vo.CusVO;
import lombok.extern.slf4j.Slf4j;
// Controller -> 자바빈으로 등록해서 관리
// 속성이 1개일 땐 속성명 생략 가능 (value= << 이 속성이 생략되어있음)
@Slf4j
@RequestMapping("/cus")
@Controller
public class CusController {
@Autowired
CusService cusService;
// void로 응답
// 요청URI : /cus/create
@GetMapping("/create")
public void create(@ModelAttribute CusVO cusVO) {
log.info("create()에 왔다");
//기본키 데이터 생성
String cusNum = this.cusService.getCusNum();
//cusVO{cusNum=CUS002, cusNm=null,cusAddr=null...}
cusVO.setCusNum(cusNum);
cusVO.setCusNm("롬롬");
// 취미 등록
List<String> hobbyList = new ArrayList<String>();
hobbyList.add("Music");
//hobbyList.add("Movie");
hobbyList.add("Sports");
cusVO.setHobbyList(hobbyList);
//forwarding
//return "cus/create"
}
// String으로 응답*
/*
요청URI : /cus/createPost
요청파라미터 : {cusNum=1234, cusNm=새롬이, postno=33233...}
요청방식 : post
*/
// String, int, Map.. => @RequestParam
// VO => ModelAttribute
// @Valid => CusVO의 validation 체크를 해주는 애너테이션
// 문제발생 시 Errors errors객체에 오류 정보를 담아서 꼭!!! forwarding 해주면 됨
@PostMapping("/createPost")
public String createPost(@Valid @ModelAttribute CusVO cusVO,
String cusNum, String cusNm, String postno, String cusAddr,
String addrDet, String cusPhe,@DateTimeFormat(pattern="yyyy-MM-dd") Date cusBir,
Errors errors) {
log.info("cusNum : " + cusNum + ", cusNm : " + cusNm +
", postno : " + postno + ", cusPhe" + cusPhe + ", cusBir" + cusBir);
// errors.hasErrors() : 문제 발생 시 true
if(errors.hasErrors()) {
return "cus/create";
}
// 고객번호는 CUS001, CUS002...
// 자동으로 다음 고객번호를 생성시켜보자
cusNum = cusService.getCusNum();
cusVO.setCusNum(cusNum);
// 입력성공 : 상세보기로 redirect. 기본키 데이터를 파라미터로 보냄
return "redirect:/cus/detail?cusNum="+cusVO.getCusNum();
}
/* 컨트롤러 매서드 매개변수
- String
- Model
- RedirectAttributes
- 자바빈즈 클래스
- MultipartFile
- BindingResult
- java.util.Locale
- java.security.Principal
*/
//요청URI : /cus/detail
@GetMapping("/detail")
public String detail( Model model, RedirectAttributes ras,
CusVO cusVO) {
//forwarding
return "cus/detail";
}
}
폼 태그 응용해보자!
form:radiobutton
form:select
<CusVO.java> 추가
- 성별 추가
//취미(여러개 선택)
private List<String> hobbyList;
//성별(한개 선택)
private String gender;
//국적(한개 선택) =>select박스
private String nationality;
<CusController.java>
// void로 응답
// 요청URI : /cus/create
@GetMapping("/create")
public void create(@ModelAttribute CusVO cusVO, Model model) {
log.info("create()에 왔다");
//기본키 데이터 생성
String cusNum = this.cusService.getCusNum();
//cusVO{cusNum=CUS002, cusNm=null,cusAddr=null...}
cusVO.setCusNum(cusNum);
cusVO.setCusNm("롬롬");
// 취미 등록
List<String> hobbyList = new ArrayList<String>();
hobbyList.add("Music");
//hobbyList.add("Movie");
hobbyList.add("Sports");
cusVO.setHobbyList(hobbyList);
// 성별 등록(남성 : male, 여성 : female, 기타 : others)
cusVO.setGender("female");
// 국적(한개 선택) => selelct박스
Map<String, String> nationalityMap = new HashMap<String, String>();
nationalityMap.put("Korea", "Korea");
nationalityMap.put("English", "English");
nationalityMap.put("Germany", "Germany");
// map은 vo에 없으므로 Model을 사용해서 값을 넣어준다.
model.addAttribute("nationalityMap",nationalityMap);
//forwarding
//return "cus/create"
}
<create.jsp>
- form:radiobutton
- form:select
<div class="mb-3">
<label for="gender" class="form-label">성별</label>
<form:radiobutton path="gender" value="male" label="male"/>
<form:radiobutton path="gender" value="female" label="female"/>
<form:radiobutton path="gender" value="others" label="others"/>
</div>
<div class="mb-3">
<label for="nationality" class="form-label">국적</label>
<form:select path="nationality" items="${nationalityMap}" />
</div>
<CusController.java>>
- createPost 수정
- hobby
// String으로 응답*
/*
요청URI : /cus/createPost
요청파라미터 : {cusNum=1234, cusNm=새롬이, postno=33233...}
요청방식 : post
*/
// String, int, Map.. => @RequestParam
// VO => ModelAttribute
// @Valid => CusVO의 validation 체크를 해주는 애너테이션
// 문제발생 시 Errors errors객체에 오류 정보를 담아서 꼭!!! forwarding 해주면 됨
@PostMapping("/createPost")
public String createPost(@Valid @ModelAttribute CusVO cusVO,
String cusNum, String cusNm, String postno, String cusAddr,
String addrDet, String cusPhe,
@DateTimeFormat(pattern="yyyy-MM-dd") Date cusBir,
String nationality, List<String> hobbyList,
Errors errors) {
log.info("cusNum : " + cusNum + ", cusNm : " + cusNm +
", postno : " + postno + ", cusPhe" + cusPhe + ", cusBir" + cusBir);
String hobby = "";
//List<String>hobbyList 처리
for(String str : hobbyList) {
hobby += str + ",";
}
cusVO.setHobby(hobby);
// errors.hasErrors() : 문제 발생 시 true
if(errors.hasErrors()) {
return "cus/create";
}
// 고객번호는 CUS001, CUS002...
// 자동으로 다음 고객번호를 생성시켜보자
cusNum = cusService.getCusNum();
cusVO.setCusNum(cusNum);
// 입력성공 : 상세보기로 redirect. 기본키 데이터를 파라미터로 보냄
return "redirect:/cus/detail?cusNum="+cusVO.getCusNum();
}
<CusVO.java>
//취미(여러개 선택){"Movie","Sprots","Read"}
private List<String> hobbyList;
//
private String hobby;
<DB추가> 컬럼추가
<cus_SQL.xml>
<!-- 고객(CUS)등록 -->
<insert id="createPost" parameterType="cusVO">
<selectKey resultType="String" order="BEFORE" keyProperty="cusNum">
SELECT NVL(SUBSTR(MAX(CUS_NUM),1,3),'CUS')
|| TRIM(TO_CHAR(NVL(SUBSTR(MAX(CUS_NUM),4),0)+1,'000'))
FROM CUS
</selectKey>
INSERT INTO CUS(CUS_NUM, CUS_NM, CUS_ADDR, CUS_PHE, POSTNO, ADDR_DET,
CUS_BIR, HOBBY, GENDER, NATIONALITY)
VALUES(
#{cusNum},#{cusNm},#{cusAddr},#{cusPhe},#{postno},#{addrDet}
,#{cusBir},#{hobby},#{gender},#{nationality}
)
</insert>
<CusMapper.java>
//<!-- 고객(CUS)등록 -->
//<insert id="createPost" parameterType="cusVO">
public int createPost(CusVO cusVO);
<CusService.java>
//고객(CUS) 등록
public int createPost(CusVO cusVO);
<CusServiceImpl.java>
//<!-- 고객(CUS)등록 -->
@Override
public int createPost(CusVO cusVO) {
return this.cusMapper.createPost(cusVO);
}
<CusController.java>
- createPost 수정
@PostMapping("/createPost")
public String createPost(@Valid @ModelAttribute CusVO cusVO,
String cusNum, String cusNm, String postno, String cusAddr,
String addrDet, String cusPhe,
@DateTimeFormat(pattern="yyyy-MM-dd") Date cusBir,
String nationality,
Errors errors) {
log.info("cusNum : " + cusNum + ", cusNm : " + cusNm +
", postno : " + postno + ", cusPhe" + cusPhe + ", cusBir" + cusBir);
String hobby = "";
//List<String>hobbyList 처리
List<String> list = cusVO.getHobbyList();
for(String str : list) {
hobby += str + ",";
}
cusVO.setHobby(hobby);
// errors.hasErrors() : 문제 발생 시 true
if(errors.hasErrors()) {
return "cus/create";
}
//CUS 테이블에 insert
int result = this.cusService.createPost(cusVO);
log.info("result : " + result);
// 입력성공 : 상세보기로 redirect. 기본키 데이터를 파라미터로 보냄
return "redirect:/cus/detail?cusNum="+cusVO.getCusNum();
}
<mybatisAlias.xml>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--
[마이바티스] 스프링에서 "_"를 사용한 컬럼명을 사용 시(BOOK 테이블의 BOOK_ID)
카멜케이스로 읽어줌(bookId)
ex) 테이블 컬럼명이 member_id인 경우 jsp화면단에서 이 값을 사용 시 memberId로 사용
-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!-- 자주 사용하는 타입의 별칭을 세팅 -->
<typeAliases>
<typeAlias type="kr.or.ddit.vo.BookVO" alias="bookVO" />
<typeAlias type="kr.or.ddit.vo.LprodVO" alias="LprodVO" />
<typeAlias type="kr.or.ddit.vo.AttachVO" alias="attachVO" />
<typeAlias type="kr.or.ddit.vo.CusVO" alias="CusVO" />
<typeAlias type="kr.or.ddit.vo.EmpVO" alias="EmpVO" />
<typeAlias type="kr.or.ddit.vo.CarVO" alias="CarVO" />
<typeAlias type="kr.or.ddit.vo.SerVO" alias="SerVO" />
</typeAliases>
</configuration>
<CusVO.java>
- @Valid
package kr.or.ddit.vo;
import java.util.Date;
import java.util.List;
import javax.validation.Valid;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.NotBlank;
import org.springframework.format.annotation.DateTimeFormat;
import lombok.Data;
//고객
/* Bean Validation이 제공하는 제약 애너테이션
- NotNull : 빈 값 체크
- *NotBlank : null 체크, trim(공백제거) 후 길이가 0인지 체크
- Size : 글자 수 체크
- Email : 이메일 주소 형식 체크
- Past : 오늘보다 과거 날짜(ex. 생일)
- Future : 미래 날짜 체크(ex. 예약일)
*/
@Data
public class CusVO {
//고객번호(필수=mandatory), null 체크, trim 후 길이가 0인지 체크
@NotBlank
private String cusNum;
//고객명
@NotBlank
@Size(min=2,max=10,message="2~10자 이내로 입력해 주세요")
private String cusNm;
//주소
private String cusAddr;
//연락처
@NotBlank
private String cusPhe;
//우편번호
private String postno;
//주소 상세
private String addrDet;
//생일
/*
2023-02-06(x) <== input type="date"
20230206(x)
--- 위에 두개는 DateTimeFormat을 써줘야 가능하다.
2023/02/06(o)(기본형식이라 DateTimeFormat이 없어도 됨)
*/
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date cusBir;
//취미(여러개 선택){"Movie","Sprots","Read"}
private List<String> hobbyList;
//
private String hobby;
//성별(한개 선택)
private String gender;
//국적(한개 선택) =>select박스
private String nationality;
//고객(CUS) : 자동차(CAR) = 1 : N
@Valid
private List<CarVO> carVOList;
//고객(CUS) : 서비스(SER) = 1 : N
private List<SerVO> serVOList;
}
자동차 정보도 받자
<div class="card" style="width: 18rem;">
<div class="card-header">
Featured
</div>
<ul class="list-group list-group-flush">
<li class="list-group-item">An item</li>
<li class="list-group-item">A second item</li>
<li class="list-group-item">A third item</li>
</ul>
</div>
<input type="text" class="form-control" aria-label="Sizing example input" aria-describedby="inputGroup-sizing-sm">
부트스트랩 넣자
<create.jsp>
- 국적 밑에 추가
<div class="mb-3">
<!-- 고객 : 소유 자동차(List<CarVO> carVOList = 1 : N
name="carVOList[0].carNum" : 중접된 자바빈 , list 의 객체의 멤버변수
-->
<div class="card" style="width: 100%;">
<div class="card-header">
소유자동차
</div>
<ul class="list-group list-group-flush">
<li class="list-group-item">
<input type="hidden" name="carVOList[0].cusNum" value="${cusNum}">
<input type="text" class="form-control" name="carVOList[0].carNum"
style="width:25%;float:left;" placeholder="자동차번호" required />
<input type="text" class="form-control" name="carVOList[0].mnfNum"
style="width:25%;float:left;" placeholder="제조 번호" required />
<input type="date" class="form-control" name="carVOList[0].dt"
style="width:25%;float:left;" placeholder="연식" />
<input type="text" class="form-control" name="carVOList[0].dist"
style="width:25%;float:left;" placeholder="주행거리" />
</li>
<li class="list-group-item">
<input type="hidden" name="carVOList[1].cusNum" value="${cusNum} ">
<input type="text" class="form-control" name="carVOList[1].carNum"
style="width:25%;float:left;" placeholder="자동차번호" required />
<input type="text" class="form-control" name="carVOList[1].mnfNum"
style="width:25%;float:left;" placeholder="제조 번호" required />
<input type="date" class="form-control" name="carVOList[1].dt"
style="width:25%;float:left;" placeholder="연식" />
<input type="text" class="form-control" name="carVOList[1].dist"
style="width:25%;float:left;" placeholder="주행거리" />
</li>
</ul>
</div>
</div>
<carVO.java>
package kr.or.ddit.vo;
import java.util.Date;
import java.util.List;
import org.hibernate.validator.constraints.NotBlank;
import org.springframework.format.annotation.DateTimeFormat;
import lombok.Data;
//자동차
@Data
public class CarVO {
//자동차 번호
@NotBlank
private String carNum;
//제조 번호
@NotBlank
private String mnfNum;
//연식
@DateTimeFormat(pattern="yyyy-MM-dd")
private Date dt;
//주행 거리
private int dist;
//고객 번호
@NotBlank
private String cusNum;
//자동차(CAR) : 서비스(SER) = 1 : N
private List<SerVO> serVOList;
}
<CusVO.java>
- @Valid
package kr.or.ddit.vo;
import java.util.Date;
import java.util.List;
import javax.validation.Valid;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.NotBlank;
import org.springframework.format.annotation.DateTimeFormat;
import lombok.Data;
//고객
/* Bean Validation이 제공하는 제약 애너테이션
- NotNull : 빈 값 체크
- *NotBlank : null 체크, trim(공백제거) 후 길이가 0인지 체크
- Size : 글자 수 체크
- Email : 이메일 주소 형식 체크
- Past : 오늘보다 과거 날짜(ex. 생일)
- Future : 미래 날짜 체크(ex. 예약일)
*/
@Data
public class CusVO {
//고객번호(필수=mandatory), null 체크, trim 후 길이가 0인지 체크
@NotBlank
private String cusNum;
//고객명
@NotBlank
@Size(min=2,max=10,message="2~10자 이내로 입력해 주세요")
private String cusNm;
//주소
private String cusAddr;
//연락처
@NotBlank
private String cusPhe;
//우편번호
private String postno;
//주소 상세
private String addrDet;
//생일
/*
2023-02-06(x) <== input type="date"
20230206(x)
--- 위에 두개는 DateTimeFormat을 써줘야 가능하다.
2023/02/06(o)(기본형식이라 DateTimeFormat이 없어도 됨)
*/
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date cusBir;
//취미(여러개 선택){"Movie","Sprots","Read"}
private List<String> hobbyList;
//
private String hobby;
//성별(한개 선택)
private String gender;
//국적(한개 선택) =>select박스
private String nationality;
//고객(CUS) : 자동차(CAR) = 1 : N
@Valid
private List<CarVO> carVOList;
//고객(CUS) : 서비스(SER) = 1 : N
private List<SerVO> serVOList;
}
<CusController.java>
package kr.or.ddit.controller;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import kr.or.ddit.service.CusService;
import kr.or.ddit.vo.CusVO;
import lombok.extern.slf4j.Slf4j;
// Controller -> 자바빈으로 등록해서 관리
// 속성이 1개일 땐 속성명 생략 가능 (value= << 이 속성이 생략되어있음)
@Slf4j
@RequestMapping("/cus")
@Controller
public class CusController {
@Autowired
CusService cusService;
// void로 응답
// 요청URI : /cus/create
@GetMapping("/create")
public void create(@ModelAttribute CusVO cusVO, Model model) {
log.info("create()에 왔다");
//기본키 데이터 생성
String cusNum = this.cusService.getCusNum();
//cusVO{cusNum=CUS002, cusNm=null,cusAddr=null...}
cusVO.setCusNum(cusNum);
cusVO.setCusNm("롬롬");
// 취미 등록
List<String> hobbyList = new ArrayList<String>();
hobbyList.add("Music");
//hobbyList.add("Movie");
hobbyList.add("Sports");
cusVO.setHobbyList(hobbyList);
// 성별 등록(남성 : male, 여성 : female, 기타 : others)
cusVO.setGender("female");
// 국적(한개 선택) => selelct박스
Map<String, String> nationalityMap = new HashMap<String, String>();
nationalityMap.put("Korea", "Korea");
nationalityMap.put("English", "English");
nationalityMap.put("Germany", "Germany");
// map은 vo에 없으므로 Model을 사용해서 값을 넣어준다.
model.addAttribute("nationalityMap",nationalityMap);
// 생성된 고객번호를 modle에 넣음
model.addAttribute("cusNum", cusNum);
//forwarding
//return "cus/create"
}
// String으로 응답*
/*
요청URI : /cus/createPost
요청파라미터 : {cusNum=1234, cusNm=새롬이, postno=33233...}
요청방식 : post
*/
// String, int, Map.. => @RequestParam
// VO => ModelAttribute
// @Valid => CusVO의 validation 체크를 해주는 애너테이션
// 문제발생 시 Errors errors객체에 오류 정보를 담아서 꼭!!! forwarding 해주면 됨
@PostMapping("/createPost")
public String createPost(@Valid @ModelAttribute CusVO cusVO,
String cusNum, String cusNm, String postno, String cusAddr,
String addrDet, String cusPhe,
@DateTimeFormat(pattern="yyyy-MM-dd") Date cusBir,
String nationality,
Errors errors) {
log.info("cusNum : " + cusNum + ", cusNm : " + cusNm +
", postno : " + postno + ", cusPhe" + cusPhe + ", cusBir" + cusBir);
String hobby = "";
//List<String>hobbyList 처리
List<String> list = cusVO.getHobbyList();
for(String str : list) {
hobby += str + ",";
}
cusVO.setHobby(hobby);
// errors.hasErrors() : 문제 발생 시 true
if(errors.hasErrors()) {
return "cus/create";
}
//CUS 테이블에 insert
int result = this.cusService.createPost(cusVO);
log.info("result : " + result);
// 입력성공 : 상세보기로 redirect. 기본키 데이터를 파라미터로 보냄
return "redirect:/cus/detail?cusNum="+cusVO.getCusNum();
}
/* 컨트롤러 매서드 매개변수
- String
- Model
- RedirectAttributes
- 자바빈즈 클래스
- MultipartFile
- BindingResult
- java.util.Locale
- java.security.Principal
*/
//요청URI : /cus/detail
@GetMapping("/detail")
public String detail( Model model, RedirectAttributes ras,
CusVO cusVO) {
//forwarding
return "cus/detail";
}
}
다중 insert
<cus_SQL_.xml>
<!-- 소유자동차(CAR) 등록. 다중 insert 시 update 태그를 사용함
- List<CarVO> carVOList
- insert, update, delete의 경우 resultType을 생략
-->
<update id="createPostCar" parameterType="java.util.List">
<foreach collection="list" item="carVO" open="INSERT ALL"
close="SELECT * FROM DUAL" separator=" ">
INTO CAR(CAR_NM, MMF_NUM, DT, DIST, CUS_NUM)
VALUES(#{carVO.carNum},#{carVO.mnfNum},#{carVO.dt},
#{carVO.dist},#{carVO.cusNum})
</foreach>
</update>
<CusMapper.java>
package kr.or.ddit.mapper;
import java.util.List;
import kr.or.ddit.vo.CarVO;
import kr.or.ddit.vo.CusVO;
public interface CusMapper {
// 고객테이블(CUS)의 기본키 데이터 생성
// <select id="getCusNum" resultType="String">
public String getCusNum();
//<!-- 고객(CUS)등록 -->
//<insert id="createPost" parameterType="cusVO">
public int createPost(CusVO cusVO);
// <!-- 소유자동차(CAR) 등록. 다중 insert 시 update 태그를 사용함
// <update id="createPostCar" parameterType="java.util.List">
public int createPostCar(List<CarVO> carVOList);
}
<CusServiceImpl.java>
package kr.or.ddit.service.impl;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import kr.or.ddit.mapper.CusMapper;
import kr.or.ddit.service.CusService;
import kr.or.ddit.vo.CarVO;
import kr.or.ddit.vo.CusVO;
@Service
public class CusServiceImpl implements CusService {
@Autowired
CusMapper cusMapper;
// 고객테이블(CUS)의 기본키 데이터 생성
@Override
public String getCusNum() {
return this.cusMapper.getCusNum();
}
//<!-- 고객(CUS)등록 -->
@Override
public int createPost(CusVO cusVO) {
int result = 0;
// 1. 고객(CUS) 등록 1행
result = this.cusMapper.createPost(cusVO);
// 2. 소유자돋차(CAR) 등록(N행)
List<CarVO> carVOList = cusVO.getCarVOList();
List<CarVO> carVOListNew = new ArrayList<CarVO>();
//cusNum 최신화
for(CarVO carVO : carVOList) {
carVO.setCusNum(cusVO.getCusNum());
carVOListNew.add(carVO);
}
result = result + this.cusMapper.createPostCar(carVOListNew);
return result;
}
}
BOM 테이블 만들기 (계층형 쿼리)
CREATE TABLE BOM (
ITEM_ID NUMBER NOT NULL,
PARENT_ID NUMBER,
ITEM_NAME VARCHAR2(20) NOT NULL,
ITEM_QTY NUMBER,
CONSTRAINT PK_BOM PRIMARY KEY(ITEM_ID)
);
INSERT INTO BOM VALUES ( 1001, NULL, '컴퓨터', 1);
INSERT INTO BOM VALUES ( 1002, 1001, '본체', 1);
INSERT INTO BOM VALUES ( 1003, 1001, '모니터', 1);
INSERT INTO BOM VALUES ( 1004, 1001, '프린터', 1);
COMMIT;
INSERT INTO BOM VALUES ( 1005, 1002, '메인보드', 1);
INSERT INTO BOM VALUES ( 1006, 1002, '랜카드', 1);
INSERT INTO BOM VALUES ( 1007, 1002, '파워서플라이', 1);
INSERT INTO BOM VALUES ( 1008, 1005, 'CPU', 1);
INSERT INTO BOM VALUES ( 1009, 1005, 'RAM', 1);
INSERT INTO BOM VALUES ( 1010, 1005, '그래픽카드', 1);
INSERT INTO BOM VALUES ( 1011, 1005, '기타장치', 1);
COMMIT;
- 계층형 쿼리
계층형 쿼리
SELECT ITEM_ID, PARENT_ID, ITEM_NAME, ITEM_QTY
, LEVEL LVL --자식으로 내려갈 수록 LEVEL이 증가됨
FROM BOM
START WITH PARENT_ID IS NULL
CONNECT BY PRIOR ITEM_ID = PARENT_ID
ORDER SIBLINGS BY ITEM_ID DESC;
간단하게 연습해보자
- FREEBRD 테이블 생성
SELECT SEQ, TITLE, WRITER, P_SEQ
FROM FREEBRD
START WITH P_SEQ IS NULL
CONNECT BY PRIOR SEQ = P_SEQ
ORDER SIBLINGS BY SEQ DESC;
조회해보면 최신글부터 보여진다
SELECT SEQ
, LPAD('ㄴ',2*(LEVEL-1)) || TITLE AS TITLE
, WRITER
, P_SEQ
, LEVEL LVL
FROM FREEBRD
START WITH P_SEQ IS NULL
CONNECT BY PRIOR SEQ = P_SEQ
ORDER SIBLINGS BY SEQ DESC;
DETAIL 만들기
<CusController.java>
- 디테일 화면에서 국적 출력
/* 컨트롤러 매서드 매개변수
- String
- Model
- RedirectAttributes
- 자바빈즈 클래스
- MultipartFile
- BindingResult
- java.util.Locale
- java.security.Principal
*/
//요청URI : /cus/detail
@GetMapping("/detail")
public String detail( Model model, RedirectAttributes ras,
@ModelAttribute CusVO cusVO) {
// 국적(한개 선택) => selelct박스
Map<String, String> nationalityMap = new HashMap<String, String>();
nationalityMap.put("Korea", "Korea");
nationalityMap.put("English", "English");
nationalityMap.put("Germany", "Germany");
model.addAttribute("nationalityMap",nationalityMap);
//forwarding
return "cus/detail";
}