-
myBatis(2),Lombok-22.06.28웹프로그래밍 2022. 6. 28. 23:41728x90
insert , update, delete myBatis적용해보자
<insert>
클라이언트가 PK입력 안할때 사용 selectKey --차후 상품관리할떄 필요
아이디 직접 받으니까 우리가 만들어줄 필요없어
SqlSession 이 내부에서 sqlException을 런타임 익셉션으로 바꾸고 있따
insert할때 SELECT ',' ||'#{'||SNAKETOCAMEL(COLUMN_NAME) ||'}' FROM COLS WHERE TABLE_NAME ='MEMBER';
,는 항상 앞에 있는것이 수정하기 쉽다.
=> 컬럼에 대한 상수 정의해놓음
jdbcType=VARCHAR null데이터 데이터베이스에 어떻게 집어넣줄거냐
<Member.xml , inert작업>
<mapper namespace="kr.or.ddit.member.dao.MemberDAO"> <insert id="insertMember" parameterType="MemberVO"> INSERT INTO MEMBER ( MEM_ID ,MEM_PASS ,MEM_NAME ,MEM_REGNO1 ,MEM_REGNO2 ,MEM_BIR ,MEM_ZIP ,MEM_ADD1 ,MEM_ADD2 ,MEM_HOMETEL ,MEM_COMTEL ,MEM_HP ,MEM_MAIL ,MEM_JOB ,MEM_LIKE ,MEM_MEMORIAL ,MEM_MEMORIALDAY ,MEM_MILEAGE ) VALUES ( #{memId,jdbcType=VARCHAR} ,#{memPass,jdbcType=VARCHAR} ,#{memName,jdbcType=VARCHAR} ,#{memRegno1,jdbcType=VARCHAR} ,#{memRegno2,jdbcType=VARCHAR} ,TO_DATE(#{memBir,jdbcType=DATE},'YYYY-MM_DD') ,#{memZip,jdbcType=VARCHAR} ,#{memAdd1,jdbcType=VARCHAR} ,#{memAdd2,jdbcType=VARCHAR} ,#{memHometel,jdbcType=VARCHAR} ,#{memComtel,jdbcType=VARCHAR} ,#{memHp,jdbcType=VARCHAR} ,#{memMail,jdbcType=VARCHAR} ,#{memJob,jdbcType=VARCHAR} ,#{memLike,jdbcType=VARCHAR} ,#{memMemorial,jdbcType=VARCHAR} ,TO_DATE(#{memMemorialday,jdbcType=DATE},'YYYY_MM_DD') ,1000 ) </insert> </mapper>
insert됐다는데 db에 없어 트랜잭션이 자동으로 롤백을 해버렸기에 안나오는것이다.
rollback (복원)이나 commit했어야 함
영구저장하려면 커밋을 해야함
<MemberDAOImpl>
원래코드
@Override public int insertMember(MemberVO member) { try ( SqlSession sqlSession =sqlSessionFactory.openSession(); ) { //sqlSession.insert("kr.or.ddit.member.dao.MemberDAOImpl.insertMember", member); MemberDAO mapper = sqlSession.getMapper(MemberDAO.class); return mapper.insertMember(member); } }
커밋 적용
@Override public int insertMember(MemberVO member) { try ( SqlSession sqlSession =sqlSessionFactory.openSession(); ) { //sqlSession.insert("kr.or.ddit.member.dao.MemberDAOImpl.insertMember", member); MemberDAO mapper = sqlSession.getMapper(MemberDAO.class); int rowcnt = mapper.insertMember(member); sqlSession.commit(); return rowcnt; } }
test => 들어간것을 알 수 있음
트랜잭션 관리
트랜잭션 :작업의 단위
insert, update, delete할때 필요함
- Atomicity (원자성) : 더이상 세부단위로 쪼갤 수 없음
- Consistency (일관성): 일관성은 데이터베이스의 상태가 일관되어야 한다는 성질이다
- Isolation (격리성, 고립성) : 트랜잭션과 다른 트랜잭션은 완전히 분리 , ex) 돈넣고 돈뽑는 행위동시에 일어날때
- Durability (지속성) : 지속성은 하나의 트랜잭션이 성공적으로 수행되었다면, 해당 트랜잭션에 대한 로그가 남아야하는 성질을 말한다.
@Override public int insertMember(MemberVO member) { try ( SqlSession sqlSession =sqlSessionFactory.openSession(); ) { //sqlSession.insert("kr.or.ddit.member.dao.MemberDAOImpl.insertMember", member); MemberDAO mapper = sqlSession.getMapper(MemberDAO.class); int rowcnt = mapper.insertMember(member); //sqlSession.commit(); 여기 주석처리하면 롤백 됨 return rowcnt; } }
=> 롤백이 됨
돈넣었는데 돈 넣기 전으로된것처럼 그래서 꼭 sqlSession.commit(); 잔액이 변경됐다면 그 변경된것이 유지 되야함
게시글 하나를 작성
--트랜잭션 시작 (성공 or 실패)
게시글을 작성하여 board테이블이 삽입
해당게시글에 3개의 첨부파일이 종속됨
첨부파일 하나하나를 attach테이블에 삽입
--트랜잭션 종료
DAO두개인데 누가 트랜잭션 대상? 대상은 로직하나여야한다.(트랜잭션)
그래서 dao구조가 완전 달라지기도한다. dao가 아니라 service될 수도있음
DB 코드조각 정리하기 -- 다시하기
update,delete
-업데이트 할 수 없는건 안됨 ... 얘를들면 주민번호 , PK ,비번
-delte할때 <delete> 사용가능하고 <update>도가능
<MemberDAOImpl>
@Override public int updateMember(MemberVO member) { try( SqlSession sqlSession = sqlSessionFactory.openSession(); ){ MemberDAO mapper = sqlSession.getMapper(MemberDAO.class); int rowcnt = mapper.updateMember(member); sqlSession.commit(); return rowcnt; } } @Override public int deleteMember(String memId) { try( SqlSession sqlSession = sqlSessionFactory.openSession(); ){ MemberDAO mapper = sqlSession.getMapper(MemberDAO.class); int rowcnt = mapper.deleteMember(memId); sqlSession.commit(); return rowcnt; } }
<Member.xml>
<mapper namespace="kr.or.ddit.member.dao.MemberDAO"> <update id="updateMember" parameterType="MemberVO"> UPDATE MEMBER SET MEM_NAME = #{memName,jdbcType=VARCHAR} , MEM_ZIP = #{memZip,jdbcType=CHAR} , MEM_ADD1 = #{memAdd1,jdbcType=VARCHAR} , MEM_ADD2 = #{memAdd2,jdbcType=VARCHAR} , MEM_HOMETEL = #{memHometel,jdbcType=VARCHAR} , MEM_COMTEL = #{memComtel,jdbcType=VARCHAR} , MEM_HP = #{memHp,jdbcType=VARCHAR} , MEM_MAIL = #{memMail,jdbcType=VARCHAR} , MEM_JOB = #{memJob,jdbcType=VARCHAR} , MEM_LIKE = #{memLike,jdbcType=VARCHAR} , MEM_MEMORIAL = #{memMemorial,jdbcType=VARCHAR} , MEM_MEMORIALDAY = #{memMemorialday,jdbcType=DATE} WHERE MEM_ID = #{memId,jdbcType=VARCHAR} </update> <update id="deleteMember" parameterType="string"> UPDATE MEMBER SET MEM_DELETE = 'Y' WHERE MEM_ID = #{memId} </update> </mapper>
<MemberDAOTest.java>
package kr.or.ddit.member.dao; import static org.junit.Assert.*; import java.sql.SQLException; import java.util.List; import org.junit.Before; import org.junit.Test; import kr.or.ddit.vo.MemberVO; public class MemberDAOTest { MemberDAO dao = new MemberDAOImpl(); MemberVO member; @Before public void setUp() { member = new MemberVO(); member.setMemId("a011"); member.setMemPass("java"); member.setMemName("신규"); member.setMemBir("1999-01-01"); member.setMemZip("000-000"); member.setMemAdd1("대전"); member.setMemAdd2("오류"); member.setMemHp("000-000-0000"); member.setMemMail("aa@gmail.net"); } // 매 테스트 케이스가 실행되기전에 먼저 실행 insert, //update보다 먼저 실행되어야함 만약에 실행문 4개라면 4개가 독립적으로 사용할 수 있는데이터 만들어줌 @Test public void testSelectMemberForAuth() { MemberVO inputData = new MemberVO(); inputData.setMemId("a001"); MemberVO member = dao.selectMemberForAuth(inputData); System.out.println(member); assertNotNull(member); } @Test(expected=RuntimeException.class) public void testInsertMemberThrow() { MemberVO member = new MemberVO(); dao.insertMember(member); } @Test public void testInsertMember() { int rowcnt = dao.insertMember(member); assertEquals(1, rowcnt); } @Test public void testSelectMemberList() { List<MemberVO> memberList = dao.selectMemberList(); assertNotNull(memberList); assertNotEquals(0, memberList.size()); System.out.println(memberList); } @Test public void testSelectMember() { assertNotNull(dao.selectMember("a001")); } @Test public void updateMember() { member.setMemId("a002"); int rowcnt = dao.updateMember(member); assertEquals(1, rowcnt); } @Test public void deleteMember() { int rowcnt =dao.deleteMember("a002"); assertEquals(1, rowcnt); } }
a001 delete하려고 하니 에러 -> 제약조건 위배 , 외래키때문 즉 a001사용하고 있는 다른 테이블 존재
제약조건 확인 R- 릴레이션 , a001삭제할 수 없어 a001삭제하려면 자식을 삭제해야함
<delete id="deleteMember" parameterType="string"> DELETE FROM MEMBER WHERE MEM_ID=#{memID} </delete>
가 아닌
<update id="deleteMember" parameterType="string"> UPDATE MEMBER SET MEM_DELETE='Y' WHERE MEM_ID=#{memID} </update>
탈퇴 회원 제외하고 조회
<select id="selectMemberList" resultType="MemberVO"> SELECT MEM_ID, MEM_NAME, MEM_HP , MEM_ADD1, MEM_MAIL, MEM_MILEAGE FROM MEMBER WHERE MEM_DELETE IS NULL </select>
탈퇴한사람 로그인 못하게
<select id="selectMemberForAuth" parameterType="MemberVO" resultType="MemberVO"> SELECT MEM_ID, MEM_PASS, MEM_NAME, MEM_HP, MEM_ADD1 FROM MEMBER WHERE MEM_ID = #{memId} AND MEM_DELETE IS NULL </select>
그래서 총 수정한 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="kr.or.ddit.member.dao.MemberDAO"> <select id="selectMemberForAuth" parameterType="MemberVO" resultType="MemberVO"> SELECT MEM_ID, MEM_PASS, MEM_NAME, MEM_HP, MEM_ADD1 FROM MEMBER WHERE MEM_ID = #{memId} AND MEM_DELETE IS NULL </select> <select id="selectMemberList" resultType="MemberVO"> SELECT MEM_ID, MEM_NAME, MEM_HP , MEM_ADD1, MEM_MAIL, MEM_MILEAGE FROM MEMBER WHERE MEM_DELETE IS NULL </select> <select id="selectMember" parameterType="string" resultType="MemberVO"> SELECT MEM_ID, MEM_PASS, MEM_NAME, MEM_REGNO1, MEM_REGNO2, TO_CHAR(MEM_BIR, 'YYYY-MM-DD') MEM_BIR, MEM_ZIP, MEM_ADD1, MEM_ADD2, MEM_HOMETEL, MEM_COMTEL, MEM_HP, MEM_MAIL, MEM_JOB, MEM_LIKE, MEM_MEMORIAL, TO_CHAR(MEM_MEMORIALDAY, 'YYYY-MM-DD') MEM_MEMORIALDAY, MEM_MILEAGE, MEM_DELETE FROM MEMBER WHERE MEM_ID = #{memId} </select> <insert id="insertMember" parameterType="MemberVO"> INSERT INTO MEMBER ( MEM_ID ,MEM_PASS ,MEM_NAME ,MEM_REGNO1 ,MEM_REGNO2 ,MEM_BIR ,MEM_ZIP ,MEM_ADD1 ,MEM_ADD2 ,MEM_HOMETEL ,MEM_COMTEL ,MEM_HP ,MEM_MAIL ,MEM_JOB ,MEM_LIKE ,MEM_MEMORIAL ,MEM_MEMORIALDAY ,MEM_MILEAGE ) VALUES ( #{memId,jdbcType=VARCHAR} , #{memPass,jdbcType=VARCHAR} , #{memName,jdbcType=VARCHAR} , #{memRegno1,jdbcType=VARCHAR} , #{memRegno2,jdbcType=VARCHAR} , TO_DATE(#{memBir,jdbcType=DATE}, 'YYYY-MM-DD') , #{memZip,jdbcType=VARCHAR} , #{memAdd1,jdbcType=VARCHAR} , #{memAdd2,jdbcType=VARCHAR} , #{memHometel,jdbcType=VARCHAR} , #{memComtel,jdbcType=VARCHAR} , #{memHp,jdbcType=VARCHAR} , #{memMail,jdbcType=VARCHAR} , #{memJob,jdbcType=VARCHAR} , #{memLike,jdbcType=VARCHAR} , #{memMemorial,jdbcType=VARCHAR} , TO_DATE(#{memMemorialday,jdbcType=DATE}, 'YYYY-MM-DD') , 1000 ) </insert> <update id="updateMember" parameterType="MemberVO"> UPDATE MEMBER SET MEM_NAME = #{memName,jdbcType=VARCHAR} , MEM_ZIP = #{memZip,jdbcType=CHAR} , MEM_ADD1 = #{memAdd1,jdbcType=VARCHAR} , MEM_ADD2 = #{memAdd2,jdbcType=VARCHAR} , MEM_HOMETEL = #{memHometel,jdbcType=VARCHAR} , MEM_COMTEL = #{memComtel,jdbcType=VARCHAR} , MEM_HP = #{memHp,jdbcType=VARCHAR} , MEM_MAIL = #{memMail,jdbcType=VARCHAR} , MEM_JOB = #{memJob,jdbcType=VARCHAR} , MEM_LIKE = #{memLike,jdbcType=VARCHAR} , MEM_MEMORIAL = #{memMemorial,jdbcType=VARCHAR} , MEM_MEMORIALDAY = #{memMemorialday,jdbcType=DATE} WHERE MEM_ID = #{memId,jdbcType=VARCHAR} </update> <update id="deleteMember" parameterType="string"> UPDATE MEMBER SET MEM_DELETE = 'Y' WHERE MEM_ID = #{memId} </update> </mapper>
<마이페이지 만들기 > 수정,탈퇴하기
이제 서비스 단을 건들여보자 비지니스 로직!!!
<MemberService>
package kr.or.ddit.member.service; import java.util.List; import kr.or.ddit.enumpkg.ServiceResult; import kr.or.ddit.exception.PKNotFoundException; import kr.or.ddit.vo.MemberVO; /** * 회원관리(CRUD)를 위한 Business Logic Layer * */ public interface MemberService { /** * 회원가입 로직 * @param member 가입할 회원의 정보를 가진VO * @return PKDUPLICATED , OK, FAIL */ public ServiceResult createMember(MemberVO member); public List<MemberVO> retrieveMemberList(); /** * 회원 상세 조회 * @param memId 조회할 회원 아이디 * @return 존재하지 않는 경우 , {@link PKNotFoundException}발생 */ public MemberVO retrieveMember(String memId); /** * 회원정보 수정 * @param member * @return {@link PKNotFoundException},INVALIDPASSWORD, OK, FAIL */ public ServiceResult modifyMember(MemberVO member); /** * 회원 탈퇴 * @param member 탈퇴할 회원의 아이디와 비번 * @return {@link PKNotFoundException},INVALIDPASSWORD, OK, FAIL */ public ServiceResult removeMember(MemberVO member); }
<MemberServiceImpl> 처음에 작성한 ...
@Override public ServiceResult modifyMember(MemberVO member) { ServiceResult result = null; // 존재 여부 확인 MemberVO saved =retrieveMember(member.getMemId()); String inputPass = member.getMemPass(); String savedPass= member.getMemId(); // 비밀번호 인증 if(savedPass.equals(inputPass)) { //수정 int rowcnt =memberDao.updateMember(member); result=rowcnt>0?ServiceResult.OK:ServiceResult.FAIL; }else { result =ServiceResult.INVALIDPASSWORD; } return result; } @Override public ServiceResult removeMember(MemberVO member) { ServiceResult result = null; if(memberDao.selectMemberForAuth(member)!=null) { int rowcnt =memberDao.deleteMember(member.getMemId()); if(rowcnt>0) { result =ServiceResult.OK; }else { result=ServiceResult.FAIL; } }else { result =ServiceResult.PKDUPLICATED; } return result; }
수정 한 코드
비밀번호 암호화 한다면 authenticate에서 수정하면되는것이다.
@Override public ServiceResult modifyMember(MemberVO member) { MemberVO inputData = new MemberVO(); inputData.setMemId(member.getMemId()); inputData.setMemPass(member.getMemPass()); ServiceResult result = authService.authenticate(inputData); switch (result) { case NOTEXIST: throw new PKNotFoundException(String.format("%s 에 해당하는 회원이 없음.", member.getMemId())); case INVALIDPASSWORD: break; default: int rowcnt = memberDao.updateMember(member); result = rowcnt > 0 ? ServiceResult.OK : ServiceResult.FAIL; break; } return result; } @Override public ServiceResult removeMember(MemberVO member) { ServiceResult result = authService.authenticate(member); switch (result) { case NOTEXIST: throw new PKNotFoundException(String.format("%s 에 해당하는 회원이 없음.", member.getMemId())); case INVALIDPASSWORD: break; default: int rowcnt = memberDao.deleteMember(member.getMemId()); result = rowcnt > 0 ? ServiceResult.OK : ServiceResult.FAIL; break; } return result; }
호출자에게 하나이상의 데이터 전달하고싶어 , call refernce 구조 사용
BeanUtils.copyProperties(inputData, member);
member데이터 inputData 로 보냄 멤버는 반환되지 않지만 call by refernce 호출자에게 전달이 됨
이미 상태가 반경된 inputData에 들어와서 member데이터를 가지고 있음
<MemberUpdate.java>
package kr.or.ddit.member.web; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.util.LinkedHashMap; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.beanutils.BeanUtils; import org.apache.commons.lang3.StringUtils; import kr.or.ddit.enumpkg.ServiceResult; import kr.or.ddit.member.service.MemberService; import kr.or.ddit.member.service.MemberServiceImpl; import kr.or.ddit.mvc.DelegatingViewResolver; import kr.or.ddit.vo.MemberVO; @WebServlet("/member/memberUpdate.do") public class MemberUpdateServlet extends HttpServlet { MemberService service = new MemberServiceImpl(); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 초기값을 가지고있는 수정 양식제공. member/memberForm 재활용 // 현재 사용자의 개인 정보를 데이터베이스로 부터 조회. MemberVO authMember = (MemberVO) req.getSession().getAttribute("authMember"); MemberVO member = service.retrieveMember(authMember.getMemId()); req.setAttribute("member", member); // 해당 정보를 초기값으로 수정UI 제공하기 위해 view layer로 이동 String viewName = "/member/memberForm.tiles"; new DelegatingViewResolver().viewResolve(viewName, req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 요청 파리미터에 포함된 특수문자에 대한 디코딩 방식 설정. req.setCharacterEncoding("UTF-8"); // 전달되는 여러개의 파라미터를 Domain layer 를 이용하여 바인딩. Map<String, String[]> parameterMap = req.getParameterMap(); MemberVO member = new MemberVO(); // 해당 VO 는 명령 처리가 완료되기 전까지 공유해야함. req.setAttribute("member", member); try { BeanUtils.populate(member, parameterMap); } catch (IllegalAccessException | InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 요청 데이터 검증 Map<String, String> errors = new LinkedHashMap<>(); req.setAttribute("errors", errors); boolean valid = validate(member, errors); String viewName = null; if(valid) { // 검증을 통과하면 로직을 사용하여 수정. ServiceResult result = service.modifyMember(member); switch (result) { case INVALIDPASSWORD: req.setAttribute("message", "비밀번호 오류"); viewName = "/member/memberForm.tiles"; break; case FAIL: req.setAttribute("message", "서버 오류"); viewName = "/member/memberForm.tiles"; break; default: viewName = "redirect:/myPage.do"; break; } }else { // 통과하지 못하면, 기존 입력 데이터와 검증 결과 데이터를 가지고 view layer 로 이동. viewName = "/member/memberForm.tiles"; } new DelegatingViewResolver().viewResolve(viewName, req, resp); } private boolean validate(MemberVO member, Map<String, String> errors) { boolean valid = true; if (StringUtils.isBlank(member.getMemId())) { errors.put("memId", "회원아이디 누락"); valid = false; } if (StringUtils.isBlank(member.getMemPass())) { errors.put("memPass", "비밀번호 누락"); valid = false; } if (StringUtils.isBlank(member.getMemName())) { errors.put("memName", "회원명 누락"); valid = false; } if (StringUtils.isBlank(member.getMemBir())) { errors.put("memBir", "생일 누락"); valid = false; } if (StringUtils.isBlank(member.getMemZip())) { errors.put("memZip", "우편번호 누락"); valid = false; } if (StringUtils.isBlank(member.getMemAdd1())) { errors.put("memAdd1", "주소1 누락"); valid = false; } if (StringUtils.isBlank(member.getMemAdd2())) { errors.put("memAdd2", "주소2 누락"); valid = false; } if (StringUtils.isBlank(member.getMemHp())) { errors.put("memHp", "휴대폰 누락"); valid = false; } if (StringUtils.isBlank(member.getMemMail())) { errors.put("memMail", "이메일 누락"); valid = false; } return valid; } }
<MypageServlet.java>
package kr.or.ddit.member.web; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.beanutils.BeanUtils; import kr.or.ddit.member.service.MemberService; import kr.or.ddit.member.service.MemberServiceImpl; import kr.or.ddit.mvc.DelegatingViewResolver; import kr.or.ddit.vo.MemberVO; @WebServlet("/myPage.do") public class MypageServlet extends HttpServlet{ MemberService service = new MemberServiceImpl(); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { MemberVO authMember = (MemberVO) req.getSession().getAttribute("authMember"); String memId = authMember.getMemId(); MemberVO member = service.retrieveMember(memId); req.setAttribute("member", member); String viewName = "/member/myPage.tiles"; new DelegatingViewResolver().viewResolve(viewName, req, resp); //지금 현재 상황: 검증 안하고 있구 , 로그인 안했을때 마이페이지 에러 -> 추후필터 적용 } }
<myPage.jsp>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <table class="table table-bordered"> <tr> <th>회원아이디</th> <td>${member.memId }</td> </tr> <tr> <th>비밀번호</th> <td>${member.memPass }</td> </tr> <tr> <th>회원명</th> <td>${member.memName }</td> </tr> <tr> <th>주민번호1</th> <td>${member.memRegno1 }</td> </tr> <tr> <th>주민번호2</th> <td>${member.memRegno2 }</td> </tr> <tr> <th>생일</th> <td>${member.memBir }</td> </tr> <tr> <th>우편번호</th> <td>${member.memZip }</td> </tr> <tr> <th>주소1</th> <td>${member.memAdd1 }</td> </tr> <tr> <th>주소2</th> <td>${member.memAdd2 }</td> </tr> <tr> <th>집전화번호</th> <td>${member.memHometel }</td> </tr> <tr> <th>회사번호</th> <td>${member.memComtel }</td> </tr> <tr> <th>휴대폰</th> <td>${member.memHp }</td> </tr> <tr> <th>이메일</th> <td>${member.memMail }</td> </tr> <tr> <th>직업</th> <td>${member.memJob }</td> </tr> <tr> <th>취미</th> <td>${member.memLike }</td> </tr> <tr> <th>기념일</th> <td>${member.memMemorial }</td> </tr> <tr> <th>기념일자</th> <td>${member.memMemorialday }</td> </tr> <tr> <th>마일리지</th> <td>${member.memMileage }</td> </tr> <tr> <th>탈퇴여부</th> <td>${member.memDelete }</td> </tr> <tr> <td colspan="2"> <input type="button" class="btn btn-primary linkBtn" value="수정" /> <input type="button" class="btn btn-danger" value="탈퇴" /> </td> </tr> </table> <script> $(".linkBtn").on("click", function(event){ location.href="${cPath}/member/memberUpdate.do"; }); </script>
<memberFrom.jsp 수정한것>
<%@page import="org.apache.commons.lang3.StringUtils"%> <%@page import="java.util.Objects"%> <%@page import="java.util.LinkedHashMap"%> <%@page import="kr.or.ddit.vo.MemberVO"%> <%@page import="java.util.Map"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <% String message =(String)request.getAttribute("message"); if(StringUtils.isNotBlank(message)){ %> <script> alert("<%=message%>"); </script> <% } %> <h4>회원 가입 양식</h4> <%-- <% MemberVO member =(MemberVO)request.getAttribute("member"); if(member==null) member = new MemberVO(); //강제로 빈 member만들어주기 Map<String,String> errors =(Map)request.getAttribute("errors"); if(errors==null) errors = new LinkedHashMap<>(); %> --%> <jsp:useBean id="member" class="kr.or.ddit.vo.MemberVO" scope="request"/> <jsp:useBean id="errors" class="java.util.LinkedHashMap" scope="request"/> <form method="post"enctype="application/x-www-form-urlencoded"> <table> <tr> <th>회원아이디</th> <td> <input type="text" name="memId" class="form-control" value='<%=Objects.toString(member.getMemId(),"") %>'/> <%--없으면 null나옴 <span class="error"><%=errors.get("memId")%></span> --%> <span class="error">${errors['memId']}</span> </td> </tr> <tr> <th>비밀번호</th> <td> <input type="text" name="memPass" value="${member.memPass }" /> <span class="error">${errors["memPass"] }</span> </td> </tr> <tr> <th>회원명</th> <td> <input type="text" name="memName" value="${member.memName }" /> <span class="error">${errors["memName"] }</span> </td> </tr> <tr> <th>주민번호1</th> <td> <input type="text" name="memRegno1" value="${member.memRegno1 }" /> <span class="error">${errors["memRegno1"] }</span> </td> </tr> <tr> <th>주민번호2</th> <td> <input type="text" name="memRegno2" value="${member.memRegno2 }" /> <span class="error">${errors["memRegno2"] }</span> </td> </tr> <tr> <th>생일</th> <td> <input type="date" name="memBir" value="${member.memBir }" /> <span class="error">${errors["memBir"] }</span> </td> </tr> <tr> <th>우편번호</th> <td> <input type="text" name="memZip" value="${member.memZip }" /> <span class="error">${errors["memZip"] }</span> </td> </tr> <tr> <th>주소1</th> <td> <input type="text" name="memAdd1" value="${member.memAdd1 }" /> <span class="error">${errors["memAdd1"] }</span> </td> </tr> <tr> <th>주소2</th> <td> <input type="text" name="memAdd2" value="${member.memAdd2 }" /> <span class="error">${errors["memAdd2"] }</span> </td> </tr> <tr> <th>집전화번호</th> <td> <input type="text" name="memHometel" value="${member.memHometel }" /> <span class="error">${errors["memHometel"] }</span> </td> </tr> <tr> <th>회사번호</th> <td> <input type="text" name="memComtel" value="${member.memComtel }" /> <span class="error">${errors["memComtel"] }</span> </td> </tr> <tr> <th>휴대폰</th> <td> <input type="text" name="memHp" value="${member.memHp }" /> <span class="error">${errors["memHp"] }</span> </td> </tr> <tr> <th>이메일</th> <td> <input type="text" name="memMail" value="${member.memMail }" /> <span class="error">${errors["memMail"] }</span> </td> </tr> <tr> <th>직업</th> <td> <input type="text" name="memJob" value="${member.memJob }" /> <span class="error">${errors["memJob"] }</span> </td> </tr> <tr> <th>취미</th> <td> <input type="text" name="memLike" value="${member.memLike }" /> <span class="error">${errors["memLike"] }</span> </td> </tr> <tr> <th>기념일</th> <td> <input type="text" name="memMemorial" value="${member.memMemorial }" /> <span class="error">${errors["memMemorial"] }</span> </td> </tr> <tr> <th>기념일자</th> <td> <input type="date" name="memMemorialday" value="${member.memMemorialday }" /> <span class="error">${errors["memMemorialday"] }</span> </td> </tr> <tr> <th>마일리지</th> <td> <input type="number" name="memMileage" value="${member.memMileage }" /> <span class="error">${errors["memMileage"] }</span> </td> </tr> <tr> <th>탈퇴여부</th> <td> <input type="text" name="memDelete" value="${member.memDelete }" /> <span class="error">${errors["memDelete"] }</span> </td> </tr> <tr> <td colspan="2"> <input type="submit" class="btn btn-primary" value="저장" /> <input type="reset" class="btn btn-warning" value="취소" /> </td> </tr> </table> </form>
탈퇴는 추후에 하고
추가 수업
* 회원관리를 위한 Domain Layer
* 한 사람의 회원에 대한 모든 정보를 가진 객체
* 기본정보 + 구매기록(상품들)
*
* MyBatis를 이용한 조인방법
* 1. 테이블간의 관계성을 메인 테이블을 중심으로 파악.
* ex) 한명의 회원과 그 사람의 구매기록 조회, 거래처명 동시 조회.
* MEMBER(1) CART(N) PROD(N) main테이블은 Member 언제나 1로 놓고 판단
* MEMBER(1) PROD(N) -> 1 : N 한사람이 여러개 상품 조회
* PROD(1) BUYER(1) -> 1 : 1 상품하나당 거래처 1
* 2. 각 테이블의 스키마를 반영한 VO생성
* MEMBER(MemberVO), PROD(ProdVO), BUYER(BuyerVO)
* 3. 테이블간의 관계성을 VO에 반영.
* 1:1 -> ProdVO has a BuyerVO
* 1:N -> has many구조 MemberVO has many ProdVO
* ----> 2,3번이 되면 ORM구조
* 4. 조인쿼리 작성 -> resultType대신 resultMap 사용해 바인딩
* 1:1 -> has a -> association 으로 바인딩
* 1:N -> has many -> collection으로 바인딩. -> id로 중복여부 판단 설정<DB구문>
--b001 사용자의 기본정보와 해당 사용자의 구매 상품 목록을 함께 조회/ --> member
--구매상품 목록(상품코드, 상품명, 상품분류명, 상품 거래처명, 구매가 ,판매가) -->prod
-- member와 prod 는 연결성이 없고 중간 관계 테이블인 cart통해서WITH PRODVIEW AS( SELECT PROD.*,BUYER_NAME, LPROD_NM FROM PROD INNER JOIN BUYER ON(PROD_BUYER = BUYER_ID) INNER JOIN LPROD ON(PROD_LGU = LPROD_GU) ), CARTVIEW AS( SELECT DISTINCT CART_MEMBER, CART_PROD FROM CART ), CARTPROD AS( SELECT CARTVIEW.*, PRODVIEW.* FROM CARTVIEW INNER JOIN PRODVIEW ON (CART_PROD = PROD_ID) ) SELECT MEMBER.*, PROD_ID, PROD_NAME, BUYER_NAME, LPROD_NM, PROD_COST, PROD_PRICE FROM MEMBER LEFT OUTER JOIN CARTPROD ON (MEM_ID = CART_MEMBER) WHERE MEM_ID = 'b001';
어떻게 이 데이터를 가져갈것인지
resultType=MemverVo prod이런거 못담지 어떻게하면 릴레이션구조를 도메인에 반영할 것이냐
구매기록담을 수 없어 b001상품여러개 구매 여러건 상품정보 담을 수 있는 property필요
Project Lombok은 자동으로 편집기에 연결하고 도구를 빌드하여 Java를 향상시키는 Java 라이브러리입니다.
vo규약 몰라도 vo만들 수 있음
<lombok 설치하기>
플러그인 라이브러리 있어야함
먼저 플러그인 다운로드
https://projectlombok.org/download
플러그인 다운받은것 -> 디드라이브에 옮겨 놓고 그곳에서 cmd창열기
lo하고 탭
java -jar lombok.jar
Specify location 누르기 => 실행할 이클립스 선택
설치완료확인 하기
=> eclipse ini에서 확인할 수 있다.
이번엔 라이브러리 추가하기
=> install에서 Maven 누르고 여기 복사해서 pom.xml넣기
=> 라이브러리 추가됨
<!-- javaBean 지원용 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.24</version> </dependency>
아이디를 통해서 동일하다는것 적용
@EqualsAndHashCode : 다프로퍼티를 비교해서 같은 상품이라 생각한다.
@EqualsAndHashCode(of= {"prodId"})
@ToString
String 출력을해야하는녀석 안해야하는 녀석 존재
긴 내용들은 콘솔에서 출력하다가 퍼포먼스 줄어듬
@ToString(exclude= {"prodDetail"}) -> 얘는 출력하지마
여러개라면 배열처럼 써주면 됨
지우면 다빠져나감
@Setter @Getter 대신 @Data를 써도된다.
@Data 이거써주면 돌아옴 (다 만들어짐)
자바빈 규약 그래도 알아두기!!!
<MemberVo>
package kr.or.ddit.vo; import java.io.Serializable; import java.util.List; import java.util.Set; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; //DTO, marker interface : Serializable - marker annotation /** * 회원관리를 위한 Domain Layer * 한 사람의 회원에 대한 모든 정보를 가진 객체 * 기본정보 + 구매기록(상품들) */ @Data @EqualsAndHashCode(of= {"memId", "memRegno1","memRegno2"}) @ToString(exclude= {"memPass","memRegno1"," memRegno2","butList"}) public class MemberVO implements Serializable { private String memId; private transient String memPass; private String memName; private transient String memRegno1; private transient String memRegno2; private String memBir; private String memZip; private String memAdd1; private String memAdd2; private String memHometel; private String memComtel; private String memHp; private String memMail; private String memJob; private String memLike; private String memMemorial; private String memMemorialday; private Integer memMileage; private String memDelete; //구매기록 private Set<List<ProdVO>> buyList; }
Member.xml수정중..
<resultMap type="MemberVO" id="memberMap"> <result property="memId" column="MEM_ID"/> </resultMap>
자동으로 되는걸 수동으로 바꾸는것
<resultMap type="MemberVO" id="memberMap" autoMapping="true"> </resultMap>
단순바인딩으로 바꿀 수 있다. 하지만 buyList는 따로 바인딩 해줘야함
<resultMap type="MemberVO" id="memberMap" autoMapping="true"> <collection property="buyList" javaType="set"></collection> </resultMap>
collection -has many
바인딩 해야하는 형태가 컬렉션이면 컬렉션 사용
<resultMap type="MemberVO" id="memberMap" autoMapping="true"> <collection property="buyList" javaType="hashSet" ofType="ProdVO"> <result property="prodId" column="PROD_ID"/> </collection> </resultMap> oftype적용하면 -- 밑에처럼 됨 <resultMap type="MemberVO" id="memberMap" autoMapping="true"> <collection property="buyList" javaType="hashSet" ofType="ProdVO" autoMapping="true"> </collection> </resultMap>
셋을 buyList에 넣어줌
ProdVO 같은지 안같은지 판단 암만 set넣어놓고도 equsals만들어놓우묜 언됨
<resultMap type="MemberVO" id="memberMap" autoMapping="true"> <id property="memId" column="MEM_ID"/> <collection property="buyList" javaType="java.util.HashSet" ofType="ProdVO" autoMapping="true"/> </resultMap>
<MemberDaoTest>
//수정 @Test public void testSelectMember() { MemberVO member = dao.selectMember("a001"); assertNotNull(member); System.out.println(member); System.out.println(member.getBuyList()); }
<myPage.jsp>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <table class="table table-bordered"> <tr> <th>회원아이디</th> <td>${member.memId }</td> </tr> <tr> <th>비밀번호</th> <td>${member.memPass }</td> </tr> <tr> <th>회원명</th> <td>${member.memName }</td> </tr> <tr> <th>주민번호1</th> <td>${member.memRegno1 }</td> </tr> <tr> <th>주민번호2</th> <td>${member.memRegno2 }</td> </tr> <tr> <th>생일</th> <td>${member.memBir }</td> </tr> <tr> <th>우편번호</th> <td>${member.memZip }</td> </tr> <tr> <th>주소1</th> <td>${member.memAdd1 }</td> </tr> <tr> <th>주소2</th> <td>${member.memAdd2 }</td> </tr> <tr> <th>집전화번호</th> <td>${member.memHometel }</td> </tr> <tr> <th>회사번호</th> <td>${member.memComtel }</td> </tr> <tr> <th>휴대폰</th> <td>${member.memHp }</td> </tr> <tr> <th>이메일</th> <td>${member.memMail }</td> </tr> <tr> <th>직업</th> <td>${member.memJob }</td> </tr> <tr> <th>취미</th> <td>${member.memLike }</td> </tr> <tr> <th>기념일</th> <td>${member.memMemorial }</td> </tr> <tr> <th>기념일자</th> <td>${member.memMemorialday }</td> </tr> <tr> <th>마일리지</th> <td>${member.memMileage }</td> </tr> <tr> <th>탈퇴여부</th> <td>${member.memDelete }</td> </tr> <tr> <td colspan="2"> <input type="button" class="btn btn-primary linkBtn" value="수정" /> <input type="button" class="btn btn-danger" value="탈퇴" /> </td> </tr> <tr> <th>구매기록</th> <td> <table> <thead> <tr> <th>상품명</th> <th>분류명</th> <th>거래처명</th> <th>구매가</th> <th>판매가</th> </tr> </thead> <tbody> <c:set var="buyList" value="${member.buyList }" /> <c:if test="${not empty buyList }"> <c:forEach items="${buyList }" var="prod"> <tr> <td>${prod.prodName }</td> <td>${prod.lprodNm }</td> <td>${prod.buyer.buyerName }</td> <td>${prod.prodCost }</td> <td>${prod.prodPrice }</td> </tr> </c:forEach> </c:if> <c:if test="${empty buyList }"> <tr> <td colspan="5">구매기록이 없음.</td> </tr> </c:if> </tbody> </table> </td> </tr> </table> <script> $(".linkBtn").on("click", function(event){ location.href="${cPath}/member/memberUpdate.do"; }); </script>
<ProdVO>
package kr.or.ddit.vo; import java.io.Serializable; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; import lombok.ToString; /** * 상품에 관한 정보를 가진 Domain Layer * */ @Data //@Setter //@Getter //of는 equal hashcode의 파라미터처럼 사용됨, 배열 //Mybatis에서 set형태의 javaType을 사용하려면 equals를 무조건 사용해야함 @EqualsAndHashCode(of= {"prodId"}) @ToString(exclude= {"prodDetail"}) public class ProdVO implements Serializable { private String prodId; private String prodName; private String prodLgu; private String lprodNm; //추가 문제점 거래처에서 가져올 수 있는 데이터 발생하면 또 가져오기 힘듦 private String prodBuyer; private BuyerVO buyer; // 추가has a private Integer prodCost; private Integer prodPrice; private Integer prodSale; private String prodOutline; private String prodDetail; private String prodImg; private Integer prodTotalstock; private String prodInsdate; private Integer prodProperstock; private String prodSize; private String prodColor; private String prodDelivery; private String prodUnit; private Integer prodQtyin; private Integer prodQtysale; private Integer prodMileage; }
<BuyerVO>
package kr.or.ddit.vo; import lombok.Data; import lombok.EqualsAndHashCode; /** * 거래처에 대한 정보를 가진 Domain Layer * */ @Data @EqualsAndHashCode(of="buyerId") public class BuyerVO { private String buyerId; private String buyerName; private String buyerLgu; private String buyerBank; private String buyerBankno; private String buyerBankname; private String buyerZip; private String buyerAdd1; private String buyerAdd2; private String buyerComtel; private String buyerFax; private String buyerMail; private String buyerCharger; private String buyerTelext; }
Member.xml
<association property="buyer" javaType="BuyerVO" > <result property="buyerName" column="BUYER_NAME"/> </association> <association property="buyer" javaType="BuyerVO" autoMapping="true"> </association>
<Member.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="kr.or.ddit.member.dao.MemberDAO"> <select id="selectMemberForAuth" parameterType="MemberVO" resultType="MemberVO"> SELECT MEM_ID, MEM_PASS, MEM_NAME, MEM_HP, MEM_ADD1 FROM MEMBER WHERE MEM_ID = #{memId} AND MEM_DELETE IS NULL </select> <select id="selectMemberList" resultType="MemberVO"> SELECT MEM_ID, MEM_NAME, MEM_HP , MEM_ADD1, MEM_MAIL, MEM_MILEAGE FROM MEMBER WHERE MEM_DELETE IS NULL </select> <resultMap type="MemberVO" id="memberMap" autoMapping="true"> <id property="memId" column="MEM_ID"/> <collection property="buyList" javaType="java.util.HashSet" ofType="ProdVO" autoMapping="true"> <association property="buyer" javaType="BuyerVO" autoMapping="true" /> </collection> </resultMap> <select id="selectMember" parameterType="string" resultMap="memberMap"> WITH PRODVIEW AS ( SELECT PROD.*, BUYER_NAME, LPROD_NM FROM PROD INNER JOIN BUYER ON(PROD_BUYER = BUYER_ID) INNER JOIN LPROD ON(PROD_LGU = LPROD_GU) ), CARTVIEW AS( SELECT DISTINCT CART_MEMBER, CART_PROD FROM CART ), CARTPROD AS( SELECT CARTVIEW.*, PRODVIEW.* FROM CARTVIEW INNER JOIN PRODVIEW ON (CART_PROD = PROD_ID) ) SELECT MEM_ID, MEM_PASS, MEM_NAME, MEM_REGNO1, MEM_REGNO2, TO_CHAR(MEM_BIR, 'YYYY-MM-DD') MEM_BIR, MEM_ZIP, MEM_ADD1, MEM_ADD2, MEM_HOMETEL, MEM_COMTEL, MEM_HP, MEM_MAIL, MEM_JOB, MEM_LIKE, MEM_MEMORIAL, TO_CHAR(MEM_MEMORIALDAY, 'YYYY-MM-DD') MEM_MEMORIALDAY, MEM_MILEAGE, MEM_DELETE , PROD_ID, PROD_NAME, PROD_COST, PROD_PRICE , BUYER_NAME, LPROD_NM FROM MEMBER LEFT OUTER JOIN CARTPROD ON (MEM_ID = CART_MEMBER) WHERE MEM_ID = #{memId} </select> <insert id="insertMember" parameterType="MemberVO"> INSERT INTO MEMBER ( MEM_ID ,MEM_PASS ,MEM_NAME ,MEM_REGNO1 ,MEM_REGNO2 ,MEM_BIR ,MEM_ZIP ,MEM_ADD1 ,MEM_ADD2 ,MEM_HOMETEL ,MEM_COMTEL ,MEM_HP ,MEM_MAIL ,MEM_JOB ,MEM_LIKE ,MEM_MEMORIAL ,MEM_MEMORIALDAY ,MEM_MILEAGE ) VALUES ( #{memId,jdbcType=VARCHAR} , #{memPass,jdbcType=VARCHAR} , #{memName,jdbcType=VARCHAR} , #{memRegno1,jdbcType=VARCHAR} , #{memRegno2,jdbcType=VARCHAR} , TO_DATE(#{memBir,jdbcType=DATE}, 'YYYY-MM-DD') , #{memZip,jdbcType=VARCHAR} , #{memAdd1,jdbcType=VARCHAR} , #{memAdd2,jdbcType=VARCHAR} , #{memHometel,jdbcType=VARCHAR} , #{memComtel,jdbcType=VARCHAR} , #{memHp,jdbcType=VARCHAR} , #{memMail,jdbcType=VARCHAR} , #{memJob,jdbcType=VARCHAR} , #{memLike,jdbcType=VARCHAR} , #{memMemorial,jdbcType=VARCHAR} , TO_DATE(#{memMemorialday,jdbcType=DATE}, 'YYYY-MM-DD') , 1000 ) </insert> <update id="updateMember" parameterType="MemberVO"> UPDATE MEMBER SET MEM_NAME = #{memName,jdbcType=VARCHAR} , MEM_ZIP = #{memZip,jdbcType=CHAR} , MEM_ADD1 = #{memAdd1,jdbcType=VARCHAR} , MEM_ADD2 = #{memAdd2,jdbcType=VARCHAR} , MEM_HOMETEL = #{memHometel,jdbcType=VARCHAR} , MEM_COMTEL = #{memComtel,jdbcType=VARCHAR} , MEM_HP = #{memHp,jdbcType=VARCHAR} , MEM_MAIL = #{memMail,jdbcType=VARCHAR} , MEM_JOB = #{memJob,jdbcType=VARCHAR} , MEM_LIKE = #{memLike,jdbcType=VARCHAR} , MEM_MEMORIAL = #{memMemorial,jdbcType=VARCHAR} , MEM_MEMORIALDAY = #{memMemorialday,jdbcType=DATE} WHERE MEM_ID = #{memId,jdbcType=VARCHAR} </update> <update id="deleteMember" parameterType="string"> UPDATE MEMBER SET MEM_DELETE = 'Y' WHERE MEM_ID = #{memId} </update> </mapper>
map안에 has a has many관계로 넣어준것임
대부분 ...
1:1 이너조인
1:M 아웃터
728x90'웹프로그래밍' 카테고리의 다른 글
회원탈퇴, Builder pattern,Reflection-22.06.29 (0) 2022.06.29 MyBatis, DBCP(DataBase Connection Pool)-0627(2) (0) 2022.06.28 view에대한 공부, modal -0627 (0) 2022.06.28 번외 ) grid적용하는 예제 이해하기 (0) 2022.06.27 tiles를 적용한 후 에러잡기 (0) 2022.06.25