Spring 실습 14일차(모델을 통한 데이터 전달)
어제 작성했던 cardList와 address를 출력
<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%-- <p>${member}</p> --%>
<!-- --MEMBER : ADDRESS = 1 : 1 (association)
-- member.address.userId(자바빈객체.중첩된 자바빈객체.프로퍼티)
address = Address(userId = null , posetCode = 080908
, location = seoul),
-->
<!-- model.addAttribute("member", -->
<p>member.address.postCode
: ${member.address.postCode}
</p>
<p>member.address.location
: ${member.address.location}
</p>
<h4>member.cardList</h4>
<c:forEach var="card" items="${member.cardList}" varStatus="stat">
<p>card${stat.count}.no : ${card.no}</p>
<p>card${stat.count}.validMonth : ${card.validMonth}</p>
</c:forEach>
c:forEach를 통해 cardList에 담긴 객체들을 card라는 이름으로 하나하나 꺼내온다.
stat.count는 1부터 반복하여 List에 담긴 객체의 갯수만큼 증가한다.
응용(Lprod)
VO 내에서 회원에게 할당된 1 : N VO 연결을 했을 경우(Ex. 중첩된 자바빈),
DB 테이블을 두 개 사용하여 JOIN 문을 사용해야 한다.
우선 1:N의 쿼리문부터 작성해보자
--LPROD : PRODUCT = 1 : N
SELECT A.LPROD_ID, A.LPROD_GU, A.LPROD_NM
, B.PRODUCT_ID, B.PNAME, B.UNIT_PRICE, B.DESCRIPTION, B.MANUFACTURER
, B.CATEGORY, B.UNITS_IN_STOCK, B.CONDITION, B.FILENAME, B.QUANTITY
FROM LPROD A LEFT OUTER JOIN PRODUCT B ON(A.LPROD_GU = SUBSTR(B.PRODUCT_ID,1,4))
WHERE A.LPROD_GU = 'P101';
Controller의 detail 메소드 수정
@RequestMapping("/detail")
public ModelAndView detail(ModelAndView mav, LprodVO lprodVO) {
log.info("detail로 연결");
// 상품 분류 목록
lprodVO = this.lprodService.detail(lprodVO);
log.info("lprodVO : " + lprodVO);
// productVOList를 넣어보자
/*
P1014 iPhone 6s 800000
P1015 LG PC 그램 1500000
P1016 Galaxy Tab S 900000
*/
List<ProductVO> productVOList = new ArrayList<ProductVO>();
// 첫번째 상품
ProductVO productVO1 = new ProductVO();
productVO1.setProductId("P1014");
productVO1.setPname("iPhone 6s");
productVO1.setUnitPrice(800000);
// 두번째 상품
ProductVO productVO2 = new ProductVO();
productVO2.setProductId("P1015");
productVO2.setPname("LG PC 그램");
productVO2.setUnitPrice(1500000);
// 세번째 상품
ProductVO productVO3 = new ProductVO();
productVO3.setProductId("P1016");
productVO3.setPname("Galaxy Tab S");
productVO3.setUnitPrice(900000);
productVOList.add(productVO1);
productVOList.add(productVO2);
productVOList.add(productVO3);
lprodVO.setProductVOList(productVOList);
log.info("lprodVO(상품 추가) : " + lprodVO);
mav.addObject("lprodVO", lprodVO);
mav.setViewName("lprod/detail");
return mav;
}
우선 하드코딩으로 직접 productVOList에 데이터를 넣어줬다.
<h1>상품 정보</h1>
<%-- <p>${lprodVO.productVOList}</p> --%>
<table border="1">
<thead>
<tr>
<th>상품 아이디</th>
<th>상품 명</th>
<th>상품 가격</th>
</tr>
</thead>
<tbody>
<c:forEach var="product" items="${lprodVO.productVOList}" varStatus="stat">
<tr>
<td>${product.productId}</td>
<td>${product.pname}</td>
<td>${product.unitPrice}</td>
</tr>
</c:forEach>
</tbody>
</table>
detail.jsp 하단부에 테이블을 하나 만들고 c:forEach를 통해 productVOList에서 객체를 하나씩 꺼내 product로 담아둔다.
담아둔 product에서 id, name, price값을 꺼내온다.
이제 위 작업을 쿼리문을 이용해 뽑아오도록 할 것
lprod_SQL.xml의 detail 부분을 위 작성해놨던 SQL 코드로 변경한다.
<!-- lprod : 1 -->
<resultMap type="lprodVO" id="lprodMap">
<result property="lprodId" column="LPROD_ID"/>
<result property="lprodGu" column="LPROD_GU"/>
<result property="lprodNm" column="LPROD_NM"/>
<collection property="productVOList" resultMap="productMap"></collection>
</resultMap>
<!-- product : N -->
<resultMap type="productVO" id="productMap">
<result property="productId" column="PRODUCT_ID"/>
<result property="pname" column="PNAME"/>
<result property="unitPrice" column="UNIT_PRICE"/>
<result property="description" column="DESCRIPTION"/>
<result property="manufacturer" column="MANUFACTURER"/>
<result property="category" column="CATEGORY"/>
<result property="unitsInStock" column="UNITS_IN_STOCK"/>
<result property="condition" column="CONDITION"/>
<result property="filename" column="FILENAME"/>
<result property="quantity" column="QUANTITY"/>
</resultMap>
<select id="detail" resultMap="lprodMap" parameterType="lprodVO">
<!-- LPROD : PRODUCT = 1 : N -->
SELECT A.LPROD_ID, A.LPROD_GU, A.LPROD_NM
, B.PRODUCT_ID, B.PNAME, B.UNIT_PRICE, B.DESCRIPTION, B.MANUFACTURER
, B.CATEGORY, B.UNITS_IN_STOCK, B.CONDITION, B.FILENAME, B.QUANTITY
FROM LPROD A LEFT OUTER JOIN PRODUCT B ON(A.LPROD_GU = SUBSTR(B.PRODUCT_ID,1,4))
WHERE A.LPROD_GU = #{lprodGu};
</select>
JOIN을 하는 순간 resultType -> resultMap으로 변경되어야 한다.
1 : N 의 경우 <collection> , 1 : 1 의 경우 <association> 을 사용하여 접근한다.
위와 같이 xml 코드를 작성해주고 아까 만든 컨트롤러 부분을 주석처리해주자.
스프링 폼 태크 라이브러리
HTML 폼을 출력하려면 <form : form> 요소를 사용한다.
registerForm.jsp를 만들어 준 다음 상단에 하단 코드를 넣어준다.
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
// 스프링 폼
// 2. 폼 요소
// 화면에 전달할 데이터를 위해 모델을 매개변수로 지정함
@GetMapping("/registerForm01")
public String registerForm01(Model model) {
// 속성명에 "member"를 지정하고 폼 객체를 모델에 추가함
// form:form modelAttribute="member"
model.addAttribute("member",new Member());
return "registerForm01";
}
<form:form>
<form:form modelAttribute="member" method="post" action="/registerForm01Post">
</form:form>
modelAttribute의 값과 controller에서 model에 넣은 속성명이 일치해야한다.
member 객체 생성할 때 기본값이 같이 담긴다.
<form:input>
<form:input path="userId" placeholder="아이디" />
<input type="text" name="userId" placeholder="아이디"/>
위, 아래 코드는 같은 구조를 가진다..
<form:input>의 path는 <input>의 name과 같다.
<form:button>
<form:button name="register">등록</form:button>
<input type="submit" value="등록" />
위 아래 코드는 같은 구조를 가진다. 위 코드가 border가 더 굵다
registerForm02
@GetMapping("/registerForm02")
public String registerForm02(Model model) {
//폼 객체의 속성명과 스프링 폼 태그의
// modelAttribute 속성값이 일치하지 않으면
// 에러 발생
model.addAttribute("user",new Member());
return "registerForm02";
}
modelArrtibute의 값과 addAttribute로 넣은 속성명이 맞지 않아 발생하는 오류
modelAttribute의 값을 user로 바꿔주던가, addAttribute의 속성명을 member로 바꾸어줘야 한다.
registerForm03
// 컨트롤러 메서드의 매개변수로 자바빈즈 객체가
// 전달이 되면 기본적으로 다시 화면으로 전달
// 컨트롤러는 기본적으로 자바빈즈 규칙에 맞는 객체는
// 다시 화면으로 폼 객체를 전달함
@GetMapping("/registerForm05")
public String registerForm05(Member member) {
return "registerForm05";
}
model.addAttribute를 사용하지 않고 자바빈을 선언한 후 modelAttribute와 이름을 맞추면 오류없이 실행된다.
// 컨트롤러 메서드의 매개변수로 자바빈즈 객체가
// 전달이 되면 기본적으로 다시 화면으로 전달
// 컨트롤러는 기본적으로 자바빈즈 규칙에 맞는 객체는
// 다시 화면으로 폼 객체를 전달함
// 2) 폼 객체의 속성명은 직접 지정하지 않으면 폼 객체의
// 클래스명의 맨처음 문자를 소문자로 변환하여 처리함
// 3) ModelAttribute 애너테이션으로 폼 객체의 속성명을
// 직접 지정할 수 있음
@GetMapping("/registerForm05")
public String registerForm05(Member user) {
return "registerForm05";
}
2) 폼 객체의 속성명은 직접 지정하지 않으면 폼 객체의 클래스명의 맨처음 문자를 소문자로 변환하여 처리함
Member user와 modelAttribute의 값을 맞추지 않아도 클래스명(Member)를 소문자로 자동 변환하여 처리하기 때문에 오류나지 않는다.
그러나 거의 맞춰주는 편이 좋다.
// 컨트롤러 메서드의 매개변수로 자바빈즈 객체가
// 전달이 되면 기본적으로 다시 화면으로 전달
// 컨트롤러는 기본적으로 자바빈즈 규칙에 맞는 객체는
// 다시 화면으로 폼 객체를 전달함
// 2) 폼 객체의 속성명은 직접 지정하지 않으면 폼 객체의
// 클래스명의 맨처음 문자를 소문자로 변환하여 처리함
// 3) ModelAttribute 애너테이션으로 폼 객체의 속성명을
// 직접 지정할 수 있음
@GetMapping("/registerForm05")
public String registerForm05(@ModelAttribute("member") Member user) {
return "registerForm05";
}
3) ModelAttribute 애너테이션으로 폼 객체의 속성명을 직접 지정할 수 있음
@ ModelAttribute(" ") : " " 안의 이름은 modelAttribute와 맞춰주어야 한다.
폼 객체의 프로퍼티에 값을 지정
@GetMapping("/registerForm05")
public String registerForm05(@ModelAttribute("member") Member member) {
// 폼 객체의 프로퍼티에 값을 지정
member.setUserId("gaeddong");
member.setUserName("개똥이");
return "registerForm05";
}
input:password
@GetMapping("/registerForm05")
public String registerForm05(@ModelAttribute("member") Member member) {
// 폼 객체의 프로퍼티에 값을 지정
member.setUserId("gaeddong");
member.setUserName("개똥이");
member.setPassword("java");
return "registerForm05";
}
<p> 패스워드 :
<!-- <input type="password" id="password" name="password"> -->
<!-- 값을 설정해서 뷰에 전달하더라도 패스워드 필드에 반영안됨 -->
<form:password path="password"/>
</p>
input:textArea
@GetMapping("/registerForm05")
public String registerForm05(@ModelAttribute("user") Member member) {
// 폼 객체의 프로퍼티에 값을 지정
member.setUserId("gaeddong");
member.setUserName("개똥이");
member.setPassword("java");
member.setIntroduction("저는 개똥이입니다\n반갑습니다.");
return "registerForm05";
}
<p> 소개글 :
<form:textarea path="introduction" rows="6" cols="30"/>
</p>
Map타입의 데이터를 추가한 후 전달
@GetMapping("/registerForm05")
public String registerForm05(@ModelAttribute("user") Member member) {
// 폼 객체의 프로퍼티에 값을 지정
member.setUserId("gaeddong");
member.setUserName("개똥이");
member.setPassword("java");
member.setIntroduction("저는 개똥이입니다\n반갑습니다.");
// 모델에 Map 타입의 데이터를 생성하여 추가한 후에 화면에 전달
Map<String, String> hobbyMap = new HashMap<String, String>();
hobbyMap.put("kmj","Music");
hobbyMap.put("ksy","Football");
hobbyMap.put("jmj","Book");
member.setHobbyMap(hobbyMap);
Map<String, String> carMap = new HashMap<String, String>();
carMap.put("qm5", "qm5");
carMap.put("sm6", "sm6");
carMap.put("volvo", "volvo");
member.setCarMap(carMap);
return "registerForm05";
}
<p> 취미 :
<form:checkboxes items="${user.hobbyMap}" path="hobbyList"/>
</p>
<p> 보유 자동차 :
<form:checkboxes items="${user.carMap}" path="carList"/>
</p>
items = '${ modelAttribute값.map이름 }'의 형태로 작성해야한다.
체크박스에 체크되어있게 만들어주기
String[] hobbyArray = {"kmj"};
member.setHobbyArray(hobbyArray);
String[] carArray = {"volvo"};
member.setCarArray(carArray);
<p> 취미 :
<form:checkboxes items="${user.hobbyMap}" path="hobbyArray"/> // path가 Array로 바뀜
</p>
<p> 보유 자동차 :
<form:checkboxes items="${user.carMap}" path="carArray"/> // path가 Array로 바뀜
</p>
<form:checkbox>
<!-- Controller -> String hobby -> kmj, jmj -->
<form:checkbox path="hobby" value="kmj" label="Music"/>
<form:checkbox path="hobby" value="ksy" label="Football"/>
<form:checkbox path="hobby" value="jmj" label="Book"/>
<p/>
<!-- Controller -> String[] hobbyArray -> ["kmj", "jmj"] -->
<form:checkbox path="hobbyArray" value="kmj" label="Music"/>
<form:checkbox path="hobbyArray" value="ksy" label="Football"/>
<form:checkbox path="hobbyArray" value="jmj" label="Book"/>
컨트롤러에 아래 코드 추가
member.setHobby("kmj");
private String[] hobbyArray;
private String hobby;
VO에 따라 path에 들어갈 값이 바뀌어야 한다.
<input:checkbox> - 2
<p> 개발자 :
<form:checkbox path="developer" value="Y"
label=" Y/N" />
</p>
<p> 외국인 여부
<form:checkbox path="foreigner" value="false"
label=" true/false" />
</p>
개발자 : 체크하면 Y, 안하면 N
외국인 여부 : 체크하면 true, 안하면 false
<form:radiobuttons>
Map<String, String> genderCodeMap = new HashMap<String, String>();
genderCodeMap.put("Male","남성");
genderCodeMap.put("Female","여성");
genderCodeMap.put("etc","기타");
member.setGenderCodeMap(genderCodeMap);
// 미리 선택 처리
member.setGender("Female");
<p> 성별 :
<form:radiobuttons items="${user.genderCodeMap}" path="gender"/>
</p>
<form:radiobutton>
<form:radiobutton path="gender" value="Male" label="남성"/>
<form:radiobutton path="gender" value="Female" label="여성"/>
<form:radiobutton path="gender" value="etc" label="기타"/>
<form:select>
Map<String, String> notionalityCodeMap = new HashMap<String, String>();
notionalityCodeMap.put("Korea","한국");
notionalityCodeMap.put("Germany","독일");
notionalityCodeMap.put("Australia","오스트레일리아");
notionalityCodeMap.put("Canada","캐나다");
member.setNotionalityCodeMap(notionalityCodeMap);
member.setNationality("Korea");
<p> 국적 :
<form:select items="${user.notionalityCodeMap}" path="nationality"/>
</p>