복습

web.xml

- 프로젝트 환경설정 파일

- Dynamic, Maven, Spring 프로젝트 모두 가지고 있는 환경설정 파일

- Apache Tomcat 이 시작되면 자동으로 실행된다

- Spring 환경설정 파일 2개를 불러서 연쇄적으로 실행시켜준다

 

Controller 클래스

- 여러개를 만들 수 있다, 단 @RequestMapping 이름값은 다르게 설정해야함

- 한가지만 만들어 사용할 수도 있다

 

@RequestParam 어노테이션

- 자동으로 앞에서 넘어온 값을 받아서 뒤의 변수에 저장한다

- 앞에서 넘어온 name 값과 매개변수 변수명이 같으면 @RequestParam 을 생략해도 된다

- 형변환할 필요 없이 @RequestParam 뒤에 쓰는 자료형에 자동 형변환 된다

ex) 여기선 String 형으로 자동형변환되어 저장됨

 

@ModelAttribute 어노테이션

- 한꺼번에 값을 받아서 @ModelAttribute 뒤에서 선언된 매개변수인 DTO 객체에 저장해준다

- 다만, DTO 객체의 필드명과 입력양식의 name 값들이 일치할때만 가능하다!

- 그땐 @ModelAttribute 생략도 가능하다

 

@Autowired

- @inject 어노테이션과 같은 역할

- 이걸 필요한 객체이자 프로퍼티 위에 쓰면 자동으로 객체 생성 및 주입까지 완료된다

- 객체 생성 및 주입 시점 : web.xml 에서 servlet-context.xml 을 불러올떄 모두 완료됨

+ servlet-context.xml 안에서 base-package 를 설정함

 

업로드 기능

- Spring 에선 fileupload 라이브러리를 많이 써서 첨부파일 업로드를 한다

 

cos 라이브러리 vs fileupload 라이브러리

cos 라이브러리

- 중복 문제를 자동으로 해결해준다

- 업로드 시켜주는 코드가 따로 없다

- 객체를 생성하면 업로드 된다

 

fileupload 라이브러리

- 중복 문제를 개발자가 해결해야한다

- 업로드 하는 코드가 따로 있다

 

ViewResolver

- servlet-context.xml 부분

	<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
	<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<beans:property name="prefix" value="/WEB-INF/views/" />
		<beans:property name="suffix" value=".jsp" />
	</beans:bean>

- servlet-context.xml 안에서 View 파일들의 위치를 설정함

- 여기선 /WEB-INF/views/ 폴더로 지정

- View 파일들이 저장될 최상위 디렉토리를 정의


Spring Web Application 예제 2 (이어서)

프로젝트 ch07

 

파일 설명

- LoginCheck.java : Intercepter Class (구현 클래스)

- UploadController.java : Controller Class

 

인터셉터 (Interceptor)

- 각 페이지마다 세션이 있는지 확인하고 로그인폼으로 보내는 대신 인터셉터를 사용한다

- 어떤 이름으로 요청이 들어올때만 DispatcherServlet 에서 Controller 로 가기 전에 해당 요청을 가로챈다

- 주로 로그인 해야만 쓸 수 있는 기능에 대해 (정보수정, 회원탈퇴, 로그아웃) 매핑을 잡아서 세션이 있는지 확인하기 위해 인터셉터 사용

 

인터셉터 설정

- servlet-context.xml 부분

1. 인터셉터 클래스로 bean 을 하나 만든다

2. 인터셉터 클래스가 어떤 경우에 동작할지 매핑을 아래에서 수행한다

3. "/upload.do" 로 요청할때 인터셉터를 동작시키고, 위에서 만든 bean 의 id 값과 연결시켜줌

- 주로 로그인 해야만 쓸 수 있는 기능에 대해 (정보수정, 회원탈퇴, 로그아웃) 매핑을 잡아서 세션이 있는지 확인하기 위해 인터셉터 사용

 

인터셉터 구현 방법

1. abstract class HandlerInterceptorAdapter 클래스를 상속 받아서 구현

- 3개의 메소드 중 필요한 메소드만 오버라이딩

2. interface HandlerInterceptor 인터페이스를 상속 받아서 구현

- 인터페이스를 상속받았을때는 3개의 메소드를 모두 메소드 오버라이딩 해야한다

 

- 다음으로 3개의 메소드를 오버라이딩해서 원하는 기능을 작성

- boolean preHandle()

- void postHandle()

- void afterCompletion()

- 메소드 오버라이딩 했으므로 이름과 형식을 그대로 따라야함

 

인터셉터 메소드 호출 시점

- preHandle() 메소드 : Client 가 요청했을때 DispatcherServlet -> Controller 로 가기전에 호출됨

ex) 여기서는 preHandle() 안에, 세션이 있는지 확인하고 없다면 로그인폼으로 보내는 코드를 작성

- postHandle() 메소드 : Controller 에서 돌아가려고 Dispatcher 로 갈때 호출됨 

- afterCompletion() 메소드 : 완전히 View 페이지에 간 후에 호출됨

 

- LoginCheck.java

package com.ch.ch07;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class LoginCheck implements HandlerInterceptor {
	
	// DispatcherServlet의 화면 처리가 완료된 상태에서 처리
	public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
			throws Exception {
	}

	// 지정된 컨트롤러의 동작 이후에 처리, Spring MVC의 Front Controller인 DispatcherServlet의 화면 처리가 완료된 상태에서 처리
	public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
			throws Exception {
	}

	// 지정된 컨트롤러의 동작 이전에 가로채는 역할 (세션이 없으면, 로그인 폼으로 이동 하도록 해준다 )
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {
		HttpSession session = request.getSession();
		if (session.getAttribute("id") == null) { // 세션이 없으면
			response.sendRedirect("loginForm.do"); // loginform 으로 이동
			return false;
		}
		return true;
	}
}

- preHandle() 메소드 안에서 세션 객체를 구한다

- 세션에 id 값이 있는지 확인 후 없다면 비정상 요청으로 판단해 loginForm.do 로 요청, 그 후 return false 하면 메소드를 빠져나감

- 세션값이 있다면 return true 를 통해, 정상적으로 원래 가던 Controller 클래스로 간다

- 로그인 해야만 쓸 수 있는 기능에 대해 (정보수정, 회원탈퇴, 로그아웃) 매핑을 잡아서 세션이 있는지 확인하기 위해 인터셉터 사용

 

인터셉터 활용

- index.jsp 를 실행하면 "upload.do" 로 요청

- Controller 클래스로 가기 전에 인터셉터가 가로챔

- 세션이 없으므로 loginForm.do 로 요청해서 loginform.jsp 로 이동한다

- 그래서 index.jsp 실행시 로그인 폼으로 가게 되는 것이다

 


"upload.do" 요청이 들어왔을때 가는 곳

- 같은 이름값 요청이지만 가는 곳이 다르다

- UploadController.java 부분

- 요청이름값이 같더라도 인터셉터 영향 또는 어떤 방식(GET/POST) 으로 요청되었는지에 따라 다른 내용이 실행된다

1) index.jsp 에서 "upload.do" 요청시 인터셉터가 잡아서 upload.jsp 가 아닌 loginform.jsp 로 간다

2) 로그인 성공 후 '업로드' 를 눌러 "upload.do" 요청시, 세션이 있으므로 인터셉터가 잡지못하고 Controller 클래스에 온다, 그 후 위의 @RequestMapping 에 의해 uploadForm() 을 실행하고 upload.jsp 로 가게된다

3) upload.jsp 에서 파일을 선택 후 '확인' 하면 "upload.do" 요청이 되고, 세션이 있으므로 인터셉터가 잡지 못하고 Controller 클래스에 온다, 그 후 아래의 @RequestMapping 에 의해 upload() 를 실행하고 uploadResult.jsp 로 가게 된다


틀린 비밀번호 입력시

1. loginForm.jsp 에서 비밀번호 입력하고 '확인'을 누르면 "login.do" 로 요청된다

2. Controller 에서 Model 객체에 "msg" 값을 저장하고, loginForm.jsp 로 이동한다

+ Controller 클래스에서는 로그인폼에서 온 입력값을 @RequestParam 으로 바로 매개변수에 저장

3. 다시 loginForm.jsp 로 이동했을때는 아래의 if 태그를 만족해서 msg 인 "똑바로 입력해" 를 출력

- 즉 한번 Controller 클래스로 갔다와야 msg 가 있으므로 msg 를 출력한다

- 처음 index.jsp 를 실행해서 loginForm.jsp 로 왔을때는 이 msg 가 없으므로 출력되지 않음

 

맞는 비밀번호 입력시

1. loginForm.jsp 에서 비밀번호 입력하고 '확인'을 누르면 "login.do" 로 요청된다

2. Controller 에서 id 값을 세션영역 공유설정하고, loginSuccess.jsp 로 간다

- 여기서부터 세션 영역이 시작된다

+ 매개변수에 Session 객체를 적기만 하면 자동으로 Session 객체가 구해진다, Spring 에선 getSession() 을 쓸 필요 없다

- 로그인 성공해서 세션 영역이 시작되었다

- 위 사진은 loginSuccess.jsp 이다

 

- loginSuccess.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ include file="header.jsp"%>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h2 class="text-primary">로그인 성공</h2>
	<a href="upload.do" class="button button-success">업로드</a>
</body>
</html>

 

- '업로드' 메뉴를 선택하면 "upload.do" 로 요청한다

 

- "upload.do" 로 요청했으므로 인터셉터로 간다

+ 메소드 오버라이딩이므로, 메소드 형식을 유지해야한다, 그래서 Session 객체를 매개변수에 써서 자동으로 구하지 않고 직접 구하고 있다

- 세션값이 존재하므로 return true; 되어 원래 가던 Controller 클래스로 간다

- GET 방식 요청이므로 위의 uploadForm() 코드가 실행된다

- upload.jsp 로 간다

 

- upload.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ include file="header.jsp"%>

<!DOCTYPE html>
<html>
<head>
<meta charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	<div class="container">
		<h2 class="text-primary">파일 업로드</h2>
		<form action="upload.do" method="post" enctype="multipart/form-data">
			<table class="table table-bordered">
				<tr>
					<th>파일</th>
					<td><input type="file" name="file"></td>
				</tr>
				<tr>
					<th colspan="2"><input type="submit" value="확인"></th>
				</tr>
			</table>
		</form>
	</div>
</body>
</html>

- 첨부파일을 전송할때는 form 태그 안에 enctype="multipart/form-data" 를 반드시 쓴다

+ 첨부파일이 없을때는 enctype="multipart/form-data" 를 써선 안된다

- 첨부파일을 전송할때는 반드시 form 태그의 method 를 "post" 로 설정해야한다

- 첨부파일의 input type 은 "file" 이다

+ 파일의 name 값이 "file" 임을 주의해서 보자

- 첨부파일을 선택하고 '확인' 을 클릭시 action 인 "upload.do" 로 요청된다

- "upload.do" 로 요청했으므로 인터셉터에 걸린다

- 세션값이 존재하므로 return true; 되어 원래 가던 Controller 클래스로 간다

- 이번에는 POST 방식으로 요청이 왔으므로 아래의 upload() 가 실행된다

- 이 upload() 에서 첨부파일을 업로드 한다, 자세히 보자

 

첨부파일 업로드

- UploadController.java 에서 upload() 메소드 부분만

	@RequestMapping(value = "upload.do", method = RequestMethod.POST)
	public String upload(@RequestParam("file") MultipartFile mf, Model model, HttpSession session)
			throws IllegalStateException, IOException {
		
		String fileName = mf.getOriginalFilename();
		int fileSize = (int) mf.getSize(); // 단위 : Byte
		
		// 첨부파일 업로드
		// mf.transferTo(new File("/path/"+fileName));
		String path = session.getServletContext().getRealPath("/upload");
		System.out.println("path : " + path);
		
		FileOutputStream fos = new FileOutputStream(path + "/" + fileName);
		fos.write(mf.getBytes());
		fos.close();
		
		model.addAttribute("fileName", fileName);
		model.addAttribute("fileSize", fileSize);
		return "uploadResult";
	}

- fileupload 라이브러리 사용해서 첨부파일 업로드

- MultipartFile 은 fileupload 라이브러리에서 지원되는 클래스명, 파일 업로드를 하기 위한 클래스이며 import 되어있음

import org.springframework.web.multipart.MultipartFile;

- @RequestParam("file") 을 통해 MultipartFile 객체 mf 를 매개변수로 받아온다, upload.jsp 폼에서 사용자가 선택한 파일을 구해온 것

- 그럼 이 mf 는 사용자가 선택한 첨부파일에 대한 정보를 이미 가지고 있게 된다

 

파일명, 파일크기, 첨부파일 외 다른 입력값 구하기 (UploadController.java 부분)

		String fileName = mf.getOriginalFilename(); // 파일명
		int fileSize = (int) mf.getSize(); // 파일크기, 단위 : Byte

- mf.getOriginalFilename() 으로 사용자가 저장한 원래 파일명을 구해서 변수 fileName 저장

- mf.getSize() 는 첨부파일의 크기를 반환, 리턴자료형이 LONG 형(8 byte) 이다, 그래서 int 형으로 다운캐스팅해서 int 변수 fileSize 에 저장, 파일 크기 단위는 Byte 이다

+ 현재는 첨부파일 외 다른 입력값이 없다

- 만약 작성자명, 이메일주소, 제목, 내용등의 입력값들이 있다면 @ModelAttribute 어노테이션으로 DTO 객체에 세팅해서 가져오면 된다

 

 

실제 업로드되는 폴더 경로 구하기 (UploadController.java 부분)

		String path = session.getServletContext().getRealPath("/upload");

- Session 객체를 통해 getServletContext().getRealPath("/upload") 로 upload 폴더의 진짜 절대경로를 구해서 path 변수에 저장한다

 

업로드하는 방법 2가지 (UploadController.java 부분)

1.

mf.transferTo(new File("/path/"+fileName));

- 1줄로 처리 가능, 업로드 시켜주는 코드이다

2.

		FileOutputStream fos = new FileOutputStream(path + "/" + fileName);
		fos.write(mf.getBytes());
		fos.close();

- FileOutputStream 객체를 생성한다

- mf.getBytes() 로 파일 내용을 읽어옴

- 읽어온 내용을 write() 로 쓰고 객체를 닫음

 

- 다음은 첨부파일명과 파일크기를 Model 객체에 저장, 나중에 브라우저에 출력시키기 위해서 저장함

		model.addAttribute("fileName", fileName);
		model.addAttribute("fileSize", fileSize);

- 다음은 출력을 위해 uploadResult.jsp 로 이동

 

- uploadResult.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
	
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h2>업로드 성공</h2>
	파일명 : ${fileName }
	<p>파일크기 : ${fileSize }
</body>
</html>

- EL 로 파일명과 파일크기를 출력한다

 

- UploadController.java 에서 upload() 메소드 부분에서 upload 폴더의 진짜 경로를 구해서 출력하는 코드가 있다

- 콘솔창을 확인해서 진짜 경로를 확인해보자

- 콘솔창 확인

C:\Users\admin\Documents\workspace-sts-3.9.11.RELEASE\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\ch07\upload

- 여기가 진짜 첨부파일이 업로드 되는 upload 폴더의 절대경로이다

- 탐색기에서 확인 가능


MyBatis 연동해서 Oracle DB 연동

 

Spring - MyBatis 연동 예제 1

프로젝트 myBatis1

 

실습 준비

- 먼저 scott 계정이 활성화 되어있어야한다

- 다음으로 클라우드의 프로젝트 import 하기

 

프로젝트 구조


파일들 살펴보기 : pom.xml

 

Spring 버전

- pom.xml 을 보면 Spring 버전이 나타나있음

- Maven Repository 페이지에서 Spring 최신버전을 확인해 볼 수 있다

- 버전을 높이면 다시 최신버전을 로컬저장소로 다운받아서 사용한다

 

Connection Pool 을 쓰기 위한 라이브러리

		<!-- dbcp jdbc connection pool -->
		<dependency>
			<groupId>commons-dbcp</groupId>
			<artifactId>commons-dbcp</artifactId>
			<version>1.2.2</version>
		</dependency>
		<dependency>
			<groupId>c3p0</groupId>
			<artifactId>c3p0</artifactId>
			<version>0.9.1.2</version>
		</dependency>

- Connection Pool 을 쓰기 위한 commons-dbcp 라이브러리, c3p0 라이브러리가 추가되어있다

 

MyBatis 연동위한 라이브러리

		<!-- mybatis -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>3.2.3</version>
		</dependency>
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis-spring</artifactId>
			<version>1.2.1</version>
		</dependency>

- Spring 에서 MyBatis 를 쓰려면 MyBatis 라이브러리 뿐 아니라 MyBatis-Spring 연결 라이브러리도 필요하다


파일들 살펴보기 : web.xml

- Apache Tomcat 이 실행시 자동으로 실행되는 파일

- 코드는 생략

 

web.xml 에 들어가는 내용 3가지

1. Dispatcher Servlet 이름, 위치, 매핑 등록

- Dispatcher Servlet 클래스에 대한 위치가 등록되어있다

- Dispatcher Servlet 으로 찾아가기위한 매핑 등록

- 현재는 do 확장자로 요청할때만 Dispatcher Servlet 으로 찾아감

 

2. Spring 환경설정 파일 불러오기

- servlet-context.xml, root-context.xml 을 불러서 실행시켜준다

+ 두 파일이 resources 폴더 내에 있는 경우 classpath: 붙이기

 

3. 한글값 인코딩

- 한글값 인코딩시켜주는 필터와 필터매핑을 잡음


파일들 살펴보기 : servlet-context.xml

- web.xml 이 실행시켜준다

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
	
	<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
	<!-- Enables the Spring MVC @Controller programming model -->
	<annotation-driven />
	
	<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
	<resources mapping="/resources/**" location="/resources/" />
	
	<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
	<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<beans:property name="prefix" value="/WEB-INF/views/" />
		<beans:property name="suffix" value=".jsp" />
	</beans:bean>
		
	<context:component-scan base-package="myBatis1" />	
	
</beans:beans>

- View 파일들의 위치를 지정하고 있다

<base-package>

- base-package 는 기본적으로는 top-level 패키지 3단계로 만들어진다

- 여기선 수정해서 "myBatis1" 를 base-package 로 설정했다

- src/main/java 폴더 하위의 myBatis 폴더 안의 클래스들을 모두 읽어온다는 의미


파일들 살펴보기 : root-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	
	<!-- Root Context: defines shared resources visible to all other web components -->
		
</beans>

- 비어있다

 

DB 연동

- 현재 프로젝트인 myBatis1 프로젝트에서는 DB 연동을 MyBatis 에서 제어(처리)하도록 하므로 MyBatis 환경설정 파일 configuration.xml 에서 처리

- 즉, 현재는 root-context.xml 이 아닌 MyBatis 환경설정 파일인 configuration.xml 에서 DB 연동 처리

- 이후에 할 myBatis2 프로젝트에서는 DB 연동을 Spring 에서 제어하도록 하므로 root-context.xml 에 DB 연동 관련 bean 생성, 그땐 root-context.xml 에서 DB연동 처리

 

- 그래서 현재 myBatis1 프로젝트의 root-context.xml 은 비어있다


MyBatis 관련 내용 복습

https://laker99.tistory.com/139

 

코딩 66 일 / 2022.09.26 / MyBatis, JSP Model 1 에 MyBatis 연동

복습 Maven - Spring 프로젝트는 Maven 으로만 라이브러리 관리 가능 - Springboo 프로젝트는 Maven, Gradle 둘 다 사용 가능 Maven 파일 구조 - src/main/java 에 확장자가 Java 인 파일을 넣는다 - src/main/re..

laker99.tistory.com

 

파일들 살펴보기 : MyBatis 환경설정 파일

1. jdbc.properties

2, configuration.xml

3. Mapper 파일 (테이블 마다)

 

- MyBatis 환경설정 파일들은 resources 폴더 안에 저장되어야함

 

- 현재 프로젝트 myBatis1 프로젝트에서는 DB 연동 관련 내용을 MyBatis 에서 제어하도록 해뒀으므로 @Autowired 로 SqlSession 을 주입하고 있지 않는다

- 현재 DAO 클래스에서는 configuration.xml 을 읽어와서 그걸로 SqlSessionFactory 객체를 생성

 

1. jdbc.properties

jdbc.driverClassName=oracle.jdbc.driver.OracleDriver
jdbc.url=jdbc:oracle:thin:@127.0.0.1:1521:xe
jdbc.username=scott
jdbc.password=tiger
jdbc.maxPoolSize=20

- DB 연동에 필요한 내용

 

2. Configuration.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>
	<properties resource="jdbc.properties" />
	<typeAliases>
		<typeAlias alias="dept" type="myBatis1.model.Dept" />
	</typeAliases>
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="${jdbc.driverClassName}" />
				<property name="url" value="${jdbc.url}" />
				<property name="username" value="${jdbc.username}" />
				<property name="password" value="${jdbc.password}" />
			</dataSource>
		</environment>
	</environments>
	<mappers>
		<mapper resource="Dept.xml" />
	</mappers>
</configuration>

1) typeAlias 로 DTO 클래스 별칭 설정

2) DB 연동

- Setter DI 를 사용

3) Mapper 파일 불러오기

- 같은 폴더 resources 에 있고, 패키지가 없으므로 파일명만으로 불러옴

 

3. Mapper 파일

- Dept.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="deptns">
	
	<!-- Use type aliases to avoid typing the full classname every time. -->
	<resultMap id="deptResult"    type="dept">
		<result property="deptno" column="deptno" />
		<result property="dname"  column="dname" />
		<result property="loc"	  column="loc" />
	</resultMap>
	
	<select id="list" resultMap="deptResult">
		select * from dept order by deptno
	</select>
	
	<select id="select" parameterType="int" resultType="dept">
		select * from dept where deptno=#{deptno}
	</select>
	
	<update id="update" parameterType="dept">
		update dept set dname=#{dname},loc=#{loc} where deptno=#{deptno}
	</update>
	
	<delete id="delete" parameterType="int">
		delete from dept where deptno=#{deptno}
	</delete>
	
</mapper>

- SQL문만 따로 빼서 만든 파일

- DAO 에서 Configuration.xml 을 읽어오고, Configuration.xml 에선 이 Dept.xml 을 읽어오므로 이 SQL문을 DAO 에서 불러올 수 있다


환경설정 파일

- 현재 프로젝트 myBatis1 의 환경설정 파일은 총 6개

 

프로젝트 환경설정 파일

- web.xml

 

Spring 환경설정 파일

- servlet-context.xml

- root-context.xml

 

MyBatis 환경설정 파일

- resources 폴더 내에 위치

- jdbc.properties

- Configuration.xml

- Dept.xml


Controller / Service / DAO

+ 각각 인터페이스와 구현클래스가 존재한다 ex) DeptService 는 인터페이스, DeptServiceImpl.java 는 구현클래스

+ 각 클래스들은 용도에 따라 myBatis 폴더 하위 다른 폴더 안에 들어가있다

- servlet-context.xml 에 의해 myBatis1 폴더 내의 모든 클래스들을 읽어온다

- 이때 이 myBatis1 폴더 내의 클래스들에는 4개의 어노테이션 중 하나가 붙어있어야한다

 

- DeptController.java 부분

- Controller클래스에서는 @Autowired 를 사용해서 Service 객체를 주입하고 있음

- Service 클래스에 Setter 메소드 없어도 Controller 클래스에 Service 객체가 주입됨

 

- DeptServiceImpl.java 부분

- Service 클래스에서는 @Autowired 를 사용해서 DAO 객체를 주입하고 있음

- DAO클래스에 Setter 메소드 없어도 Service 클래스에 DAO객체가 주입됨

 

- DeptDaoImpl.java 부분

 

- 현재 프로젝트 myBatis1 프로젝트에서는 DB 연동 관련 내용을 MyBatis 에서 제어하도록 해뒀으므로 @Autowired 로 SqlSession 을 주입하고 있지 않는다

- 현재 DAO 클래스에서는 configuration.xml 을 읽어와서 그걸로 SqlSessionFactory 객체를 생성

 

+ 나중에 할 myBatis2 프로젝트 에서는 @Autowired 로 SqlSession 객체 주입할 것

 


현재 프로젝트 myBatis1 흐름

- 뒤쪽 DB 와 연동하는 것은 Model 2 - MyBatis 연동과 같은 방식으로 되어있다


프로젝트 실행

- 오라클 서버(OracleServiceXE, OracleXETNSListener) 가 구동되어 있어야한다

- 오라클 scott 계정이 활성화 되어있어야 한다


 - index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<script type="text/javascript">
	location.href="deptList.do";
</script>
</body>
</html>

- do 확장자로 요청하므로 Dispatcher Servlet 으로 잘 찾아간다

- Dispatcher Servlet -> Controller 클래스로 이동


- index.jsp 를 실행

- 부서목록을 보여주고 있다

- 부서명 중 하나 클릭시 그 부서명의 상세정보를 보여주고 수정 / 삭제 가능

+ Foreign Key 때문에 삭제가 안된다, 즉 참조하는 자식이 있으므로 부모가 지워지지 않음

+ 삭제를 위해서는 On Delete Cascade 옵션을 붙이거나 해야함



프로젝트 myBatis1 : 부서 목록 페이지

Controller 클래스 DeptControlelr

- index.jsp 에서 "deptList.do" 로 요청했으므로 Controller 클래스쪽으로 가자

- DeptController.java

package myBatis1.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import myBatis1.model.Dept;
import myBatis1.service.DeptService;

@Controller
public class DeptController {
	@Autowired
	private DeptService ds;

	// 부서 목록
	@RequestMapping("deptList.do")
	public String list(Model model) {
		List<Dept> list = ds.list();
		model.addAttribute("list", list);
		return "deptList";
	}

	// 상세 페이지
	@RequestMapping("deptView.do")
	public String deptView(int deptno, Model model) {
		Dept dept = ds.select(deptno);
		model.addAttribute("dept", dept);
		return "deptView";
	}

	// 수정 폼
	@RequestMapping("deptUpdateForm.do")
	public String deptUpdateForm(int deptno, Model model) {
		Dept dept = ds.select(deptno);
		model.addAttribute("dept", dept);
		return "deptUpdateForm";
	}
	
	// 부서 정보 수정
	@RequestMapping("deptUpdate.do")
	public String deptUpdate(Dept dept, Model model) {
		int result = ds.update(dept);
		model.addAttribute("result", result);
		return "deptUpdate";
	}
	
	// 부서 정보 삭제
	@RequestMapping("deptDelete.do")
	public String deptDelete(int deptno, Model model) {
		int result = ds.delete(deptno);
		model.addAttribute("result", result);
		return "deptDelete";
	}
}

+ @Controller, @Autowired 는 Spring 지원되는 라이브러리 클래스를 import 해야함

 

DeptController.java 에서 프로퍼티 부분만

	@Autowired
	private DeptService ds;

 

- 이 DeptService 는 Service 인터페이스이다

- 인터페이스로 주입해도 되고, 구현클래스로 주입해도 된다, 여기서는 인터페이스에 주입하고 있다

- 즉 어노테이션 기반 DI이며 이미 Service 객체 ds 에 주입이 완료되었음

+ 어노테이션 기반 DI 이므로 Service 클래스에 Setter 메소드가 없어도 여기서 주입 가능

 

DeptController.java 에서 "deptList.do" 요청 부분만 

	// 부서 목록
	@RequestMapping("deptList.do")
	public String list(Model model) {
		List<Dept> list = ds.list();
		model.addAttribute("list", list);
		return "deptList";
	}

- index.jsp 실행시 여기로 찾아옴, 자동으로 list() 실행됨

- Service 객체 ds 로 Service 의 메소드인 list() 를 호출한다, 

- 그렇게 Service 로 넘어가는 것 * Service 로 넘어가기 아래에

- 리턴받은 결과인 List 를 리스트 list 에 저장하고, Model 객체를 통해 deptList.jsp 로 그 리스트 list 를 저장한다

+ prefix, suffix 생략한 나머지인 "deptList" 를 리턴

- 그 후 Dispacher Servlet -> View 인 deptList.jsp  로 이동

 

어노테이션 기반 DI 사용 조건

1. 해당 클래스는 servlet-context.xml 에 base-package 로 지정된 myBatis1 패키지의 하위에 들어가있어야함

2. 어노테이션 4가지 중 하나가 붙어있어야함

3. @Autowired 를 주입하고자하는 프로퍼티 위에 쓰기

- 이 DeptService 는 Service 인터페이스이다


Service 구현 클래스 DeptServiceImpl

- Service 의 list() 메소드를 호출했으므로 Service 로 넘어가자

- DeptServiceImpl.java

package myBatis1.service;

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import myBatis1.dao.DeptDao;
import myBatis1.model.Dept;

@Service
public class DeptServiceImpl implements DeptService {
	@Autowired
	private DeptDao dd;

	public List<Dept> list() {
		return dd.list();
	}

	public Dept select(int deptno) {
		return dd.select(deptno);
	}

	public int update(Dept dept) {
		return dd.update(dept);
	}

	public int delete(int deptno) {
		return dd.delete(deptno);
	}
}

- @Autowired 를 통해 DAO 객체를 프로퍼티 dd 에 주입받고 있다

- DAO 객체 dd 를 통해 DAO의 list() 메소드를 호출한다 * DAO 로 이동 아래에

- DAO 클래스에서 돌아올땐 리턴받은 값을 바로 리턴함

 

- 현재는 MyBatis 쪽에서 DB 연동을 처리하므로 SqlSession 객체를 @Autowired 로 생성하지 않음

- 현재 프로젝트 myBatis1 프로젝트에서는 Mode 1 - MyBatis , Mode 2 - MyBatis 연동할때와 똑같이 연동한다

+ 나중에 할 프로젝트 myBaits2 프로젝트에서는 Spring 쪽에서 DB 연동을 처리할 것, @Autowired 로 SqlSession 객체 생성할 것


DAO 구현 클래스 DeptDaoImpl

- DAO 로 넘어가게 되므로 DAO 를 살펴보자

- DeptDaoImpl.java

package myBatis1.dao;

import java.io.IOException;
import java.io.Reader;
import java.util.List;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.springframework.stereotype.Repository;

import myBatis1.model.Dept;

@Repository
public class DeptDaoImpl implements DeptDao {
	private static SqlSession session;
	static {
	    try { 
	    	Reader reader = Resources.getResourceAsReader("configuration.xml");
		      SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(reader);
		      session = ssf.openSession(true); // auto commit
		      reader.close(); 
		} catch (IOException e) {
		  System.out.println("read file error : "+e.getMessage());
		}
	}
	public List<Dept> list() {
		return session.selectList("deptns.list");
	}
	public Dept select(int deptno) {
		return session.selectOne("deptns.select",deptno);
	}
	public int update(Dept dept) {
		return session.update("deptns.update",dept);
	}
	public int delete(int deptno) {
		return session.delete("deptns.delete",deptno);
	}
}

 

DeptDaoImpl.java 에서 SqlSession 객체 구하는 부분만

	private static SqlSession session;
	static {
	    try { 
	    	Reader reader = Resources.getResourceAsReader("configuration.xml");
		      SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(reader);
		      session = ssf.openSession(true); // auto commit
		      reader.close(); 
		} catch (IOException e) {
		  System.out.println("read file error : "+e.getMessage());
		}
	}

- SqlSession 객체를 static 을 붙여서 정적필드로 만듬

- static 으로 블럭을 만들어서 공유함, Model 1, Model 2 에서 했었던 getSession() 메소드 부분에 해당

- MyBatis 의 환경설정 파일 configuration.xml 을 읽어와서 Reader 객체 reader 생성

- reader 객체를 통해 SqlSessionFactory 객체 ssf 를 구함

- ssf.openSession(true) 를 통해 SqlSession 객체 session 을 구해옴

- openSession() 안에 true 가 들어가면 자동으로 커밋이 수행됨

 

DeptDaoImpl.java 에서 list() 부분만

	public List<Dept> list() {
		return session.selectList("deptns.list");
	}

- 리스트를 구해오기 위해서는 2개이상의 데이터에 대한 상세정보를 구해와야한다, 그래서 selectList() 를 사용

+ selecList() 의 리턴자료형은 List 이다

- deptns 는 Mapper 파일 Dept.xml 안에 설정된 namespace 를 의미, list 는 id 를 의미

- Mapper 파일인 Dept.xml 에서 id 가 list 인 SQL문을 불러오라는 의미

- 이후 Mapper 파일 에서 돌아오면 그 리스트를 그대로 Service 클래스로 리턴한다


Mapper 파일

- Mapper 파일 Dept.xml 로 넘어가보자

- Dept.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="deptns">
	
	<!-- Use type aliases to avoid typing the full classname every time. -->
	<resultMap id="deptResult"    type="dept">
		<result property="deptno" column="deptno" />
		<result property="dname"  column="dname" />
		<result property="loc"	  column="loc" />
	</resultMap>
	
	<select id="list" resultMap="deptResult">
		select * from dept order by deptno
	</select>
	
	<select id="select" parameterType="int" resultType="dept">
		select * from dept where deptno=#{deptno}
	</select>
	
	<update id="update" parameterType="dept">
		update dept set dname=#{dname},loc=#{loc} where deptno=#{deptno}
	</update>
	
	<delete id="delete" parameterType="int">
		delete from dept where deptno=#{deptno}
	</delete>
	
</mapper>

- namespace 가 "deptns"

 

Dept.xml 에서 id 가 "list" 인 태그 부분만 + resultMap

	<!-- Use type aliases to avoid typing the full classname every time. -->
	<resultMap id="deptResult"    type="dept">
		<result property="deptno" column="deptno" />
		<result property="dname"  column="dname" />
		<result property="loc"	  column="loc" />
	</resultMap>
	
	<select id="list" resultMap="deptResult">
		select * from dept order by deptno
	</select>

- resultType 대신 resultMap 을 쓰고 있다

- resultMap 태그의 id 값으로는 아래의 select 태그에서 resultMap 속성에 쓴 "deptResult" 를 써야함

- resultMap 태그의 type 값으로 DTO alias 인 "dept" 가 들어가야함

- 즉 자동으로 DTO 객체에 세팅해서 돌려주는 resultType 대신 resultMap 으로 일일히 매핑하고 있다

- Dept 테이블 컬럼명과 DTO 프로퍼티명이 다를때 resultMap 을 사용해야한다

- 현재 Dept 테이블 컬럼명과 DTO 인 Dept 클래스 프로퍼티 명이 같으므로 resultMap 안써도 된다 

+ join 을 해야할 경우 등 resultMap 을 써야만 하는 경우도 있다

- 아래의 select SQL문을 실행하고, 그 검색결과를 매핑을 통해 여러개의 DTO 객체에 세팅 하고, 그 DTO 객체들을 List 에 담고 그 List 를 DAO 로 돌려줌

 

- 다시 DAO 로 돌아가야하므로 위로 돌아가자

* 돌아올때 설명은 하늘색 하이라이트

 

- Contorller 까지 돌아간 후 Dispatcher Servlet -> View 로 가게 된다

 

View 페이지 deptList.jsp

- deptList.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ include file="header.jsp"%>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<div class="container">
		<h2 class="text-primary">부서 목록</h2>
		<table class="table table-hover">
			<tr>
				<th>부서코드</th>
				<th>부서명</th>
				<th>근무지</th>
			</tr>
			<c:if test="${not empty list }">
				<c:forEach var="dept" items="${list}">
					<tr>
						<td>${dept.deptno }</td>
						<td><a href="deptView.do?deptno=${dept.deptno}"
							   class="btn btn-info"> ${dept.dname}</a></td>
						<td>${dept.loc }</td>
					</tr>
				</c:forEach>
			</c:if>
			<c:if test="${empty list }">
				<tr>
					<th colspan="3">데이터가 없습니다</th>
				</tr>
			</c:if>
		</table>
	</div>
</body>
</html>

- Controller 클래스에서 Model 에 저장했던 리스트의 네임값이 "list"

- Model 에 저장된 것이 리스트 이므로 forEach 태그의 items 에 ${list} 를 써서 출력한다

- forEach 태그를 통해 dept 로 개별 요소를 받고 있그 ${dept.deptno}, ${dept.dname}, ${dept.loc} 으로 목록을 출력

+ 부트스트랩 적용된것임

- include 태그로 불러온 header.jsp 파일에서 bootstrap, JSTL core 라이브러리등을 불러왔다

 

상세 페이지로 이동 (deptList.jsp 부분)

<td><a href="deptView.do?deptno=${dept.deptno}"
		 class="btn btn-info"> ${dept.dname}</a></td>

- 부서명 클릭시 상세 페이지로 가기 위해 ${dept.dname} 에는 a 태그로 링크를 걸고 "deptView.do" 를 요청한다

- "deptView.do" 로 요청하면서 Primary Key 인 부서 번호 ${dept.deptno} 을 가지고 간다

+ 이때 부서번호를 저장해서 가지고 가는 변수는 "deptno" 이다!

- Dispatcher Servlet -> Controller 로 간다


프로젝트 myBatis1 : 상세 페이지

- 부서명 클릭시 상세 페이지로 가기 위해 ${dept.dname} 에는 a 태그로 링크를 걸고 "deptView.do" 를 요청한다

 

DeptController.java 에서 "deptView.do" 요청 부분만

	// 상세 페이지
	@RequestMapping("deptView.do")
	public String deptView(int deptno, Model model) {
		Dept dept = ds.select(deptno);
		model.addAttribute("dept", dept);
		return "deptView";
	}

- 넘어온 부서 번호를 저장하는 변수와 같은 이름인 "deptno" 를 매개변수에 사용하므로 넘어온 부서번호가 바로 dept 변수에 저장됨

- 즉 @RequestParam("deptno") 가 int deptno 앞에 생략된 것이다

- int deptno 에 맞춰서 형변환까지 int 형으로 되어서 자동으로 deptno 변수에 저장해줌

- Model 객체를 매개변수에 선언만하면 자동으로 Model 객체가 생성됨

- 위에서 @Autowired 로 구해온 Service 객체 ds 로 Service 클래스의 select() 메소드 호출, 호출하며 부서번호 전달

- 그럼 Service 클래스로 가게 됨 * 아래에 설명

< 돌아왔을때>

- DTO 객체를 돌려받아서 Dept 객체 dept 로 받음

- 그 객체 dept 를 Model 을 사용해서 저장하고 deptView.jsp 로 간다

+ DTO 객체가 Model 에 저장되었으므로 View 에선 ${이름값.필드명} 으로 출력함

 

Service 클래스 DeptServiceImpl.java 에서 select() 메소드 부분만

	public Dept select(int deptno) {
		return dd.select(deptno);
	}

- 위에서 주입된 DAO 객체 dd 를 사용해서 DAO 의 select() 메소드 호출, 호출하며 부서번호 그대로 전달

<돌아왔을때>

- 받은 DTO 객체를 그대로 돌려준다

 

DAO 클래스 DeptDaoImpl.java 에서 select() 메소드 부분만

	public Dept select(int deptno) {
		return session.selectOne("deptns.select",deptno);
	}

- 1개 부서에 대한 상세 정보 (1개 데이터) 를 구해오므로 selectOne() 메소드 사용

- Mapper 파일 Dept.xml 에서 id 가 select 인 SQL문을 호출

- 두번째 매개변수로 부서번호를 그대로 전달

<돌아왔을때>

- 받은 DTO 객체를 그대로 돌려준다

 

Mapper 파일 Dept.xml 에서 id 가 select 인 태그 부분만

	<select id="select" parameterType="int" resultType="dept">
		select * from dept where deptno=#{deptno}
	</select>

- 전달되는 값은 부서번호 이므로 parameterType 은 "int"

- 돌려주는 값은 DTO 객체여야 하므로 resultType 은 DTO alias 은 "dept"

- 전달되는 값인 부서번호를  SQL문에서 #{deptno} 형식으로 사용함

 

- 다시 DAO -> Service -> Controller 로 가서 Dispatcher Servlet -> View 인 deptView.jsp 로 간다

* 돌아올때 설명은 하늘색 하이라이트

 

- deptView.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ include file="header.jsp"%>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>

<!-- deptList.do 요청한 결과 페이지(deptList.jsp)의 table태그만 불러옴 -->
<script type="text/javascript">
	$(function() {
		$('#list').load("deptList.do table");
	});
</script>
</head>
<body>
	<div class="container">
		<h2>부서 상세정보</h2>
		<table class="table table-bordered">
			<tr>
				<th>부서코드</th>
				<td>${dept.deptno}</td>
			</tr>
			<tr>
				<th>부서명</th>
				<td>${dept.dname }</td>
			</tr>
			<tr>
				<th>근무지</th>
				<td>${dept.loc }</td>
			</tr>
			<tr>
				<th colspan="2"><a href="deptList.do" class="btn btn-info">목록</a>
					<a href="deptUpdateForm.do?deptno=${dept.deptno}" class="btn btn-info">수정</a> 
					<a href="deptDelete.do?deptno=${dept.deptno}" class="btn btn-info">삭제</a></th>
			</tr>
		</table>
	</div>
	<div id="list"></div>
</body>
</html>

- DTO 객체가 Model 에 저장되었으므로 ${이름값.필드명} 으로 출력함, 즉 ${dept.필드명} 으로 출력

ex) ${dept.deptno} 로 부서 번호 출력, ${dept.dname} 으로 부서명 출력

+ 표기법만 ".필드명" 이다, 의미는 getter 메소드로 가져오는 것임

ex) ${dept.dname} 은 dept.getDname() 과 같다

 

아래에 목록 출력하기 (비동기 처리) (deptView.jsp 부분)

- 아래쪽의 내용은 부서 목록 페이지와 같은 내용이 나타남

<!-- deptList.do 요청한 결과 페이지(deptList.jsp)의 table태그만 불러옴 -->
<script type="text/javascript">
	$(function() {
		$('#list').load("deptList.do table");
	});
</script>
	<div id="list"></div>

- ajax 기능 중 load() 함수를 사용해서 구현했다

- id 가 "list" 인 div 태그를 jQuery 로 불러와서 ajax의 load() 함수 사용, 파일을 불러와서 이 영역에 출력시켜라는 의미

- load() 안에는 불러올 파일명이 들어간다, index 파일에 들어간 요청과 같은 (목록 구하기)요청 인 "deptLIst.do" 를 넣는다

- 부서 목록을 출력하는 View 페이지 deptList.jsp 중에서 table 태그 안의 내용만 불러오기 위해 한칸 띄워서 " table" 도 추가 작성

+ 그래서 "부서 목록" 이라는 글자등은 나타나지 않고 딱 테이블만 나타남

 

+ 비동기로 서버에 요청하는 load() 함수

- $("출력시킬 태그").load("요청할 파일명");

- 특정 서버측의 문서를 불러옴, 안에는 특정 파일명이 들어간다

- 서버측에 비동기적으로 요청하고, 서버측의 지정된 파일의 내용을 모두 불러와서 앞의 태그에 결과 출력 시켜주는 역할

 

ajax load() 함수 관련 자세한 내용 (검색어 : load() 함수 예제 3)

https://laker99.tistory.com/105

 

목록 / 수정 / 삭제 링크걸기 (deptView.jsp 부분)

<tr>
	<th colspan="2"><a href="deptList.do" class="btn btn-info">목록</a>
	<a href="deptUpdateForm.do?deptno=${dept.deptno}" class="btn btn-info">수정</a> 
	<a href="deptDelete.do?deptno=${dept.deptno}" class="btn btn-info">삭제</a></th>
</tr>

- '목록' 버튼 클릭시 "deptList.do" 로 요청해서 부서 목록 페이지로 간다

- '수정' 버튼 클릭시 "deptUpdateForm.do" 로 요청해서 수정폼으로 간다, Primary Key 인 부서번호를 가져간다

- '삭제' 버튼 클릭시 "deptDelete.do" 로 요청해서 삭제, Primary Key 인 부서번호를 가져간다

- 현재 '삭제' 를 누르면 오류가 발생함 (참조하는 자식이 있기때문에 삭제 불가)

 

- '목록' 버튼 클릭시에 목록 페이지로 가는 것은 이미 설명했다


프로젝트 myBatis1 : 수정 폼

- '수정' 버튼을 클릭해서 수정폼으로 가보자

 

- "deptUpdateForm.do" 요청시 수정폼으로 가면서 get 방식으로 부서번호 deptno 를 가져간다

- 부서번호를 가져가야 상세 정보를 다시 구해서 수정폼에 뿌려줄 수 있다

 

DeptController.java 에서 "deptUpdateForm.do" 요청 부분만

	// 수정 폼
	@RequestMapping("deptUpdateForm.do")
	public String deptUpdateForm(int deptno, Model model) {
		Dept dept = ds.select(deptno);
		model.addAttribute("dept", dept);
		return "deptUpdateForm";
	}

+ Controller 클래스 메소드에서 리턴자료형은 대부분 String

- 부서 번호가 get 방식으로 넘어왔고, 그게 넘어온 값의 변수명과 같은 이름인 deptno 변수에 자동으로 형변환 후 저장됨

- int deptno 뒤에 @RequestParam 생략 된 것

+ View 페이지로 값을 가져갈 것이므로 Model 객체를 생성해야함, Model 객체를 매개변수에 써주면 Model 객체가 생성됨

- 위에서 @Autowired 로 주입된 Service 객체 ds 로 Service 클래스의 select() 메소드 호출, 호출하면서 부서번호 전달

- 상세정보를 가져와서 수정폼에 뿌려줘야하므로 아까 목록을 구할때 호출했던 메소드 select() 를 여기서 또 호출

- @Autowired 가 써진 객체 생성 및 주입 시점 : web.xml 에서 servlet-context.xml 을 호출할때 

<돌아올 때>

- 상세정보를 저장한 DTO 객체를 dept 로 받아서 Model 객체에 저장

- deptUpdateForm.jsp 로 이동

 

- Service 클래스로 이동한다

DeptServiceImpl.java 에서 select() 메소드 부분만

	public Dept select(int deptno) {
		return dd.select(deptno);
	}

- 아까 목록을 구할때 호출했던 메소드와 같다

DeptDaoImpl.java 에서 select() 메소드 부분만

	public Dept select(int deptno) {
		return session.selectOne("deptns.select",deptno);
	}

- 아까 목록을 구할때 호출했던 메소드와 같다

Mapper 파일 Dept.xml 에서 id 가 "select" 인 태그 부분만

	<select id="select" parameterType="int" resultType="dept">
		select * from dept where deptno=#{deptno}
	</select>

 

수정 폼 View 페이지인 deptUpdateForm.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ include file="header.jsp"%>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<div class="container" align="center">
		<h2 class="text-primary">부서정보 수정</h2>
		<form action="deptUpdate.do" method="post">
			<input type="hidden" name="deptno" value="${dept.deptno}">
			<table class="table table-striped">
				<tr>
					<th>부서코드</th>
					<td>${dept.deptno}</td>
				</tr>
				<tr>
					<th>부서명</th>
					<td><input type="text" name="dname" required="required"	value="${dept.dname }"></td>
				</tr>
				<tr>
					<th>근무지</th>
					<td><input type="text" name="loc" required="required" value="${dept.loc }"></td>
				</tr>
				<tr>
					<th colspan="2"><input type="submit" value="확인"></th>
				</tr>
			</table>
		</form>
	</div>
</body>
</html>

- Model 객체에 저장된 값이 DTO 객체이므로 ${네임값.필드명} 으로 출력한다, 여기선 ${dept.필드명} 으로 출력

- 입력양식의 value 속성으로 수정폼에 값을 뿌린다

- 수정폼 입력양식에 값을 입력하고 '확인' 을 누르면 action 인 "deptUpdate.do" 로 요청한다

- 부서 코드는 입력양식이 아닌 출력을 하고 있으므로 넘어가지 않는다, 그래서 hidden 으로 전달해야한다

- 이 값을 전달해야만 where 조건절에 deptno 를 넣어서 1개 부서를 수정 가능하다

- "deptUpdate.do" 로 요청하면서 부서 코드인 deptno 를 hidden 객체로 전달한다


프로젝트 myBatis1 : 부서 정보 수정

- 수정폼 deptUpdateForm.jsp 의 입력양식에 값을 입력하고 '확인' 을 누르면 action 인 "deptUpdate.do" 로 요청한다

 

DeptController.java 에서 "deptUpdate.do" 요청 부분만

	// 부서 정보 수정
	@RequestMapping("deptUpdate.do")
	public String deptUpdate(Dept dept, Model model) {
		int result = ds.update(dept);
		model.addAttribute("result", result);
		return "deptUpdate";
	}

- 수정폼 deptUpdateForm.jsp 에서 넘어온 값들을 DTO Dept 객체 dept 에 세팅(저장)해준다

- 즉 @ModelAttribute 어노테이션이 Dept dept 앞에 생략되어 있다

+ 넘어온 name 값과 DTO 프로퍼티명이 같은 이름인 경우에만 가능하다

- 위에서 @Autowired 로 주입한 Service 객체 ds 로 Service 클래스의 update() 메소드를 호출, 호출하며 사용자가 수정폼에 입력한 값들을 전달함

<돌아온 후>

- DB 수정을 성공하면 1 을 리턴할 것, 그 값을 result 변수에 저장한다

- 그 result 변수를 Model 객체에 저장 후, deptUpdate.jsp 로 간다

 

DeptServiceImpl.java 에서 update() 부분만

	public int update(Dept dept) {
		return dd.update(dept);
	}

- 받은 객체를 그대로 다시 전달

<돌아올때>

- 성공시 받은 1 을 다시 리턴

 

DeptDaoImpl.java 에서 update() 부분만

	public int update(Dept dept) {
		return session.update("deptns.update",dept);
	}

- 받은 객체를 그대로 다시 전달

<돌아올 때>

- 이 update() 메소드는 DB 수정 성공시 1을 자동으로 리턴함

 

Mapper 파일 Dept.xml 에서 id 가 "update" 인 태그 부분만

	<update id="update" parameterType="dept">
		update dept set dname=#{dname},loc=#{loc} where deptno=#{deptno}
	</update>

- 넘어오는 것이 DTO 객체이므로 그 값을 사용할땐 #{필드명} 으로 사용함

ex) #{loc} 은 dept.getLoc() 과 같은 의미이다

- 수행 후 돌아간다, update 문이므로 돌려주는 returnType 은 적지 않는다

 

View 페이지인 deptUpdate.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ include file="header.jsp"%>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<c:if test="${result > 0 }">
		<script type="text/javascript">
			alert("수정 성공");
			location.href = "deptList.do";
		</script>
	</c:if>
	<c:if test="${result <= 0 }">
		<script type="text/javascript">
			alert("수정 실패");
			history.go(-1);
		</script>
	</c:if>
</body>
</html>

- 수정 성공시 result 는 1 이므로 "수정 성공" 메세지를 뿌리고 "deptList.do" 로 요청해서 목록 페이지로 돌아간다

- 수정 실패시 "수정 실패" 메세지를 뿌리고 이전 페이지인 수정 폼으로 돌아간다


프로젝트 myBatis1 : 삭제

- deptView.jsp 에서 '삭제' 버튼을 클릭해서 삭제를 해보자

<a href="deptDelete.do?deptno=${dept.deptno}" class="btn btn-info">삭제</a></th>

 

- "deptDelete.do" 요청시 Controller 클래스로 가면서 get 방식으로 부서 코드 deptno 를 가져간다

- 부서코드를 가져가야 delet SQL문 실행시 where 절에 부서 코드를 넣어서 해당 부서를 삭제할 수 있다

 

DeptController.java 에서 "deltDelete.do" 요청 부분만

	// 부서 정보 삭제
	@RequestMapping("deptDelete.do")
	public String deptDelete(int deptno, Model model) {
		int result = ds.delete(deptno);
		model.addAttribute("result", result);
		return "deptDelete";
	}

- 넘어온 부서코드가 저장된 변수명과 같은 이름인 deptno 를 매개변수에 써서 바로 deptno 에 넘어온 부서코드 저장

+ @RequestParam("deptno") 가 int deptno 앞에 생략

- Service 객체 ds 를 통해 Service 클래스의 delete() 메소드를 호출함, 호출하면서 부서코드 deptno 를 전달

<돌아올 때>

- 성공여부를 확인하기 위해 result 변수로 delete() 의 리턴값을 받는다

- 그 result 를 Model 객체에 저장하고 deptDelete.jsp 로 간다

 

DeptServiceImpl.java 에서 delete() 부분만

	public int delete(int deptno) {
		return dd.delete(deptno);
	}

DeptDaoImpl.java 에서 delete() 부분만

	public int delete(int deptno) {
		return session.delete("deptns.delete",deptno);
	}

- Mapper 파일에서 id 가 "delete" 인 SQL문을 불러온다

- 전달하는 값은 부서 코드인 deptno

Mapper 파일 Dept.xml 에서 id 가 delete 인 태그 부분만

	<delete id="delete" parameterType="int">
		delete from dept where deptno=#{deptno}
	</delete>

- #{deptno} 로 가져옴


문제점

- '삭제' 누르면 오류 발생, 무결성 제약조건때문에 오류가 발생한다

- 즉, 해당 부서(부모)를 참조하는 Foreign Key (자식) 이 있으므로7 삭제가 되지 않는다

 

 

문제 해결

- SQL문에서 On Delete Cascade 를 사용해서 부모를 지울때 참조하는 자식까지 같이 지운다 


View 페이지인 deptDelete.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ include file="header.jsp"%>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<c:if test="${result > 0 }">
		<script type="text/javascript">
			alert("삭제 성공");
			location.href = "deptList.do";
		</script>
	</c:if>
	<c:if test="${result != 1 }">
		<script type="text/javascript">
			alert("삭제 실패");
			history.go(-1);
		</script>
	</c:if>
</body>
</html>

- 삭제 성공 메시지 또는 실패 메세지를 뿌리고 이동하는 역할을 해야한다

- result 가 0 보다 큰 경우, 즉 삭제에 성공한 경우엔 "삭제 성공" 출력후 "deptList.do" 로 이동, 목록 페이지로 간다

- result 가 1 이 아닌 경우는 삭제 실패를 의미하므로 "삭제 실패" 출력 후 이전 페이인 상세 페이지로 돌아감


- 오늘 한 내용은 아님, 미리 설정 해두기

 

STS 에 Data Source Management 추가

- STS 는 이클립스와 다르게 Data Source Management 가 없다

- STS 에 plug-in 기능을 추가해야 Data Source Management 를 사용 가능

 

STS 에 Data Source Management 추가하는 방법

+ Recent posts