Spring

Spring 18 - Mapper 인터페이스, 스프링 폼 태그 라이브러리, 계층형 쿼리

inderrom 2023. 2. 6. 17:39
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";
	}