Spring 실습 9일차(SQL 내 여러 테이블에 데이터 넣기)
SQL 데이터베이스 member테이블에 데이터를 몇개 넣어보자
reg_date는 기본값을 sysdate로 입력해놨다.
address 테이블도 하나 만들어준다.
-- 회원 : 주소 = 1 : 1
CREATE TABLE ADDRESS(
USER_ID VARCHAR2(20),
POST_CODE VARCHAR2(10),
LOCATION VARCHAR2(300),
CONSTRAINT PK_ADDRESS
PRIMARY KEY(USER_ID),
CONSTRAINT FK_ADDRESS
FOREIGN KEY(USER_ID)
REFERENCES MEMBER(USER_ID)
);
user_id가 address 테이블의 기본키(PK)이면서 member테이블에서 참조하는 외래키(FK)이다.
만들었으면 왼쪽 상단 파일 -> Data Modeler -> 임포트 -> 데이터 딕셔너리를 들어가준다.
난 다수리 시스템이라는 곳에 테이블이 만들어져있다. 다음으로 넘어가면
다수리 시스템에 접속하고 있는 계정을 체크한 뒤
임포트하려는 테이블을 체크하고 완료를 눌러주면
두 테이블에 대한 관계를 확인할 수 있다.
이제 ADDRESS 테이블에도 데이터를 몇개 넣어주자
※ user_id부분은 member테이블과 맞춰야 한다.
--왼쪽 외부조인(모든 회원정보 출력)
SELECT A.USER_ID, A.USER_NAME, A.PASSWORD, A.REG_DATE, A.COIN
, B.USER_ID, B.POST_CODE, B.LOCATION
FROM MEMBER A, ADDRESS B
WHERE A.USER_ID = B.USER_ID(+);
-- ANSI 표준(LEFT OUTER JOIN)
SELECT A.USER_ID, A.USER_NAME, A.PASSWORD, A.REG_DATE, A.COIN
, B.USER_ID, B.POST_CODE, B.LOCATION
FROM MEMBER A
LEFT OUTER JOIN ADDRESS B
ON A.USER_ID = B.USER_ID;
위 아래 코드 모두 동일한 결과를 출력한다.
![](https://blog.kakaocdn.net/dn/dYLUtw/btsG5TLUyKM/vdp4zvLGO4wNVetukEimYK/img.png)
이번엔 Card 테이블과 VO를 만들어 Member와 연결해보자
Card
1명의 회원이 여러개의 카드를 소지할 수 있기 때문에 타입은 List<Card>로 만들어준다.
// MEMBER : Card = 1 : N
private List<Card> cardList;
중첩된 자바빈즈로 만들어주고 card라는 이름의 VO를 만들어주자.
package kr.or.ddit.vo;
import lombok.Data;
@Data
public class Card {
// Member 자바빈 클래스의 프로퍼티(식별자)
private String userId;
private String no;
private String validMonth;
}
이제 SQL 데이터베이스에 Card 테이블을 만들어준다.
CREATE TABLE CARD(
USER_ID VARCHAR2(20),
NO VARCHAR2(30),
VALID_MONTH VARCHAR2(10),
CONSTRAINT PK_CARD
PRIMARY KEY(USER_ID, NO),
CONSTRAINT FK_CARD
FOREIGN KEY(USER_ID)
REFERENCES MEMBER(USER_ID)
);
USER_ID와 NO가 복합키로써 기본키(PK) 이면서 MEMBER 테이블에서 참조하는 외래키(FK)이다.
복합키 설명 참고
Card테이블에 데이터를 몇개 넣어준다.
SELECT A.USER_ID, A.USER_NAME, A.PASSWORD, A.REG_DATE, A.COIN
, B.USER_ID, B.NO, B.VALID_MONTH
FROM MEMBER A LEFT OUTER JOIN CARD B
ON(A.USER_ID = B.USER_ID);
이제 jsp에 카드 정보를 입력할 input요소를 만들어주자
<p>
<!-- list타입으로 받기 때문에 인덱스가 필요하다. -->
<input type="text" name="cardList[0].no" placeholder="카드1-번호" /><br />
<input type="text" name="cardList[0].validMonth" placeholder="카드1-유효기간"/><br />
<input type="text" name="cardList[1].no" placeholder="카드2-번호" /><br />
<input type="text" name="cardList[1].validMonth" placeholder="카드2-유효기간"/><br />
</p>
List<Card> 타입으로 받아주기 위해 name값에 인덱스를 표시해주어야 한다.
자기소개
jsp부분에 자기소개 입력할 수 있는 칸을 만들어주자
우선 VO에 introduction을 추가해주고
private String introduction;
jsp에 textArea로 추가해주자
<p>
<textArea rows="6" cols="50" name="introduction" placeholder="자기소개"></textArea>
</p>
이제 CRUD를 진행해줄껀데 그 전에 컨트롤러에서 member log부분을 제외한 나머지 log들은 주석처리를 해주자
CRUD
이전에 많이 했기때문에 간략하게만 설명하겠다.
MemberService, MemverServiceImpl, MemberDao, member_SQL.xml을 어노테이션을 포함해 만들어준다.
@Autowired 어노테이션으로 의존성 주입까지 완료해주자.
@ResponseBody
@PostMapping("/registerUserId")
public String registerUserId(String userId, String password
, String gender, String nationality, String cars
, String[] carArray, ArrayList<String> carList
, String hobbys, String[] hobbyArray
, ArrayList<String> hobbyList, String developer
, boolean foreigner, Address address
, Member member) {
// log.info("registerUserId");
// log.info("userId : " + userId);
// log.info("password : " + password);
// log.info("gender : " + gender);
// log.info("nationality : " + nationality);
// log.info("cars : " + cars);
// log.info("hobbys : " + hobbys);
// log.info("developer : " + developer);
// log.info("foreigner : " + foreigner);
// log.info("address : " + address);
log.info("member : " + member);
// if(carArray != null) {
// log.info("------------------");
// for(String car : carArray) {
// log.info("carArray[" + car + "]");
// }
// log.info("------------------");
// }
//
// if(hobbyArray != null) {
// log.info("------------------");
// for(String hobby : hobbyArray) {
// log.info("hobbyArray[" + hobby + "]");
// }
// log.info("------------------");
// }
// 얜 안됌ㅋㅋㅋㅋ
if(carList != null) {
log.info("------------------");
for(int i = 0; i<carList.size(); i++) {
log.info("carList[" + carList.get(i) + "]");
}
log.info("------------------");
}
// 얘도 안됌ㅋㅋㅋㅋㅋㅋㅋ
if(hobbyList != null) {
log.info("------------------");
for(int i = 0; i<hobbyList.size(); i++) {
log.info("hobbyList[" + hobbyList.get(i) + "]");
}
log.info("------------------");
}
// insert/update/delete는 Controller에서 직접 안한다.
// Service 계층에서 비즈니스 로직을 수행한다.
// Insert/Update/Delete의 리턴 타입은 int값
int result = this.memberService.registerUserId(member);
log.info("result : " + result);
return "success";
}
대충 이런 형식이 될것이다.
이제 이전에 배웠던 대로 CURD를 진행하자
하기 전에
member안에 들어있는 card와 address값은 각 테이블에 넣어주어야 하기 때문에 Impl부터 조금 수정을 해주어야 한다.
일단 mybatisAlias.xml에 가서 alias를 VO마다 설정해주자
<typeAlias type="kr.or.ddit.vo.Member" alias="member" />
<typeAlias type="kr.or.ddit.vo.Address" alias="address" />
<typeAlias type="kr.or.ddit.vo.Card" alias="card" />
Impl
package kr.or.ddit.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import kr.or.ddit.dao.MemberDao;
import kr.or.ddit.service.MemberService;
import kr.or.ddit.vo.Address;
import kr.or.ddit.vo.Card;
import kr.or.ddit.vo.Member;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
public class MemberServiceImpl implements MemberService{
@Autowired
MemberDao memberDao;
@Override
public int registerUserId(Member member) {
// 1) MEMBER
int result = this.memberDao.registerUserId(member);
log.info("registerUserId -> result: " + result);
// 2) ADDRESS
// member에서 address정보 빼오기
Address address = member.getAddress();
// address정보에서 비어있는 userId를 member에 있는 userId로 채워넣기
address.setUserId(member.getUserId());
log.info("registerUserId -> address : " + address);
// DAO에서 address를 파라미터로 받는 insert 연결
result += this.memberDao.insertAddress(address);
log.info("registerUserId -> result(2) :" + result);
// 3) Card
// member에서 cardList정보 빼오기
List<Card> cardList = member.getCardList();
for (Card card : cardList) {
// for문을 돌려 각 card에 비어있는 userId를 member에 있는 userId로 채워넣기
card.setUserId(member.getUserId());
log.info("registerUserId -> cardList2 :" + cardList);
// DAO에서 card를 파라미터로 받는 insert 연결
result += this.memberDao.insertCard(card);
}
log.info("registerUserId -> result(3) :" + result);
return result;
}
}
DAO
package kr.or.ddit.dao;
import java.util.List;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import kr.or.ddit.vo.Address;
import kr.or.ddit.vo.Card;
import kr.or.ddit.vo.Member;
@Repository
public class MemberDao {
@Autowired
SqlSessionTemplate sqlSessionTemplate;
// MEMBER 테이블에 INSERT
public int registerUserId(Member member) {
return this.sqlSessionTemplate.insert("member.registerUserId",member);
}
// ADDRESS 테이블에 INSERT
public int insertAddress(Address address) {
return this.sqlSessionTemplate.insert("member.insertAddress",address);
}
public int insertCard(Card card) {
return this.sqlSessionTemplate.insert("member.insertCard",card);
}
}
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">
<mapper namespace="member">
<!-- INSERT, UPDATE, DELETE의 resultType은 생략(생략 시 int로 반환) -->
<insert id="registerUserId" parameterType="member">
INSERT INTO MEMBER(USER_ID, USER_NAME, PASSWORD, REG_DATE, COIN)
VALUES(#{userId}, #{userName}, #{password}, SYSDATE, #{coin})
</insert>
<!-- address 테이블에 insert -->
<insert id="insertAddress" parameterType="address">
INSERT INTO ADDRESS(USER_ID, POST_CODE, LOCATION)
VALUES(#{userId}, #{postCode}, #{location})
</insert>
<!-- CARD 테이블에 INSERT
Card{userId=c001,no=111,validMonth=24/03}
-->
<insert id="insertCard" parameterType="card">
INSERT INTO ADDRESS(USER_ID, NO, VALID_MONTH)
VALUES(#{userId}, #{no}, #{validMonth})
</insert>
</mapper>
위와 같이 작성하면 각 테이블에 데이터가 들어가게 된다.
lprodId 추출
갑자기 lprod로 넘어오게 되었다(이유는 몰?루)
lprod/create 페이지에 lprodId값이 들은 input에 SQL에서 lprodId의 최댓값+1을 해서 페이지가 실행되면 자동으로 들어가게 해보자
(ajax 활용)
// create.jsp
function getLprodId(){
$.ajax({
url:"getLprodId",
type:"post",
dataType:"text",
success:function(result){
console.log("result : ",result)
$('input[name="lprodId"]').val(result);
}
})
}
$(function(){
getLprodId() // 함수 호출
)
/*
요청URI : /lprod/getLprodId
요청파라미터 :
요청방식 : post
응답타입 : int
응답데이터 : LPROD 테이블의 MAX(LPROD_ID)+1
*/
@ResponseBody
@RequestMapping(value="/getLprodId", method=RequestMethod.POST)
public int getLprodId() {
int result = this.lprodService.getLprodId();
log.info("result : " + result);
return result;
}
Service와 Impl, Dao 연결하는 방식은 스킵하겠다
<select id="getLprodId" resultType="Integer">
SELECT MAX(LPROD_ID)+1 FROM LPROD
</select>
여기가 조금 다른데 INSERT, UPDATE, DELETE의 경우 기본 반환값이 int값이라 resultType을 적어주지 않아도 되지만
select는 resultType을 Integer라고 명시해야 int값으로 반환해준다.
$('input[name="lprodId"]').val(result);
위 스크립트의 경우 jQuery 문법으로 val()의 괄호 안에 return 받은 값을 넣어주면 value값으로 들어가게 된다
$('input[name="lprodId"]').val() = result;
위와 같이 쓰면 안된다는 소리
내 SQL안의 MAX값이 12이기 때문에 + 1이 된 값인 13이 자동으로 들어가있게 된다.
상품 코드 자동 생성
+ 버튼을 누르면 상품 아이디, 상품명, 가격이 한줄씩 추가되고
- 버튼을 누르면 한줄씩 지워지는 스크립트와
상품 분류 코드를 작성하고 상품 코드 자동 생성 버튼을 클릭 시 상품 아이디 란에 자동으로 값이 채워지게 해보자
ex) 상품 아이디 2줄, 상품 분류 코드 = P502
상품 아이디1 = P5021
상품 아이디2 = P5022
<body>
<h1>상품분류 등록</h1>
<!--
요청URI : /lprod/create
요청 파라미터 :{lprodId=14, lprodGu=P501, lprodNm=분식류}
요청방식 :post
-->
<form action="/lprod/create" method="post">
<p> 상품분류 아이디 : <input type="number" name="lprodId" placeholder="상품분류 아이디(ex.14)"> </p>
<p>
상품분류 코드 : <input type="text" name="lprodGu" required placeholder="상품분류 코드(ex.P501)">
<button type="button" class="btn btn-primary btn-xs col-sm-1">상품코드 자동생성</button>
</p>
<p> 상품분류 명 : <input type="text" name="lprodNm" placeholder="상품분류 명(ex. 분식류)"> </p>
<p id="pFunc">
<button type="button" class="btn btn-info btn-sm col-sm-1" id="btnPlus" >+</button>
<button type="button" class="btn btn-danger btn-sm col-sm-1" id="btnMinus" >-</button>
</p>
<!--
1) + : 영역이 하나 생김
2) = : 마지막 영역 삭제
-->
<p class="pArea">
<input type="text" name="productVOList[0].productId" id="productId0" class="form-control col-sm-3 float-left" placeholder="상품아이디">
<input type="text" name="productVOList[0].pname" id="pname0" class="form-control col-sm-3 float-left" placeholder="상품명">
<input type="text" name="productVOList[0].unitPrice" id="unitPrice0" class="form-control col-sm-3 float-left" placeholder="가격">
</p>
<p style="clear: both;"></p>
<p>
<input type="button" id="btnSubmit" value="즉시삭제">
</p>
</form>
</body>
/*
before : 선택한 요소의 앞에 내용 삽입
after : 선택한 요소의 뒤에 내용 삽입
prepend : 선택한요소의 자식요소 앞에 내용삽입
append : 선택한요소의 자식요소 뒤에 내용삽입
*/
//상품추가
$("#btnPlus").on("click", function(){
// class 속성 값이 pArea인 요소의 갯수
let pAreaLen = $(".pArea").length;
// name값과 id값을 중복되게 하지 않기 위해 pAreaLen값을 함께 부여해준다.
let str ="<p class='pArea'>";
str +="<input type='text' name='productVOList["+pAreaLen+"].productId' id='productId"+pAreaLen+"' class='form-control col-sm-3 float-left' placeholder='상품아이디' />";
str +="<input type='text' name='productVOList["+pAreaLen+"].pname' id='pname"+pAreaLen+"' class='form-control col-sm-3 float-left' placeholder='상품명' />";
str +="<input type='text' name='productVOList["+pAreaLen+"].unitPrice' id='unitPrice"+pAreaLen+"' class='form-control col-sm-3 float-left clear-both' placeholder='가격' />";
str +="</p>";
str +="<p style='clear:both;'></p>";
// id가 btnSubmit인 요소의 위에 str을 추가한다
$("#btnSubmit").before(str);
});
//next() 메서드는 선택한 요소의 다음 형제 요소를 반환함
// 이 메서드는 DOM 요소의 다음 형제를 따라 이동함
//next() 메서드와 유관한 메서드는 nextAll()과
// nextUntil() 메서드가 있음
//nextAll() 메서드 : 선택한 요소의 다음 형제 요소를 모두 반환
//nextUntil() 메서드 : 주어진 주 매개변수 사이에 있는
// 모든 다음 형제 요소를 반환함
//<li> 요소 중에서 클래스 이름이 start인 요소의 형제 다음에 오는 요소를 선택함
// - 버튼 클릭 시
$("#btnMinus").on("click",function(){
// class 속성 값이 pArea인 요소의 갯수
let pAreaLen = $(".pArea").length;
console.log("pAreaLen",pAreaLen)
// 갯수가 1개면 삭제가 안된다.
if(pAreaLen < 2){
alert("최소 하나는 존재해야 합니다.");
return;
}
// 요소 삭제
$(".pArea").last().next().remove();
$(".pArea").last().remove();
})
// class 속성명이 btn-primary인 요소를 클릭했을 때
$('.btn-primary').on('click', function(){
let pArea = $(".pArea");
// input의 name값이 lprodGU인 요소의 value
let lprodGu = $("input[name='lprodGu']").val();
// pArea의 갯수만큼 반복
for(let i = 0; i<pArea.length; i++){
console.log(lprodGu+(i+1));
// id값이 productId + 1,2,3...인 요소의 value에 lprodGu(P502)+1,2,3...를 넣는다
// JS에서는 문자 + 숫자(문자열로 자동 변경) = 문자형식이기 때문에
// P502 + 1,2,3 ... = P5021, P5022, P5023... 이 된다.
$("input[id='productId"+i+"']").val(lprodGu+(i+1));
}
})
9일차 종료~!