프로젝트 myBatis1 vs 프로젝트 myBatis2

프로젝트 myBatis1

- 이전 프로젝트는 DAO 클래스에서 configuration.xml(MyBatis 환경설정 파일) 을 읽어와서 그걸로 SqlSessionFactory 객체를 생성

- 이전 프로젝트는 MyBatis 쪽에서 DB 연동을 처리하므로 SqlSession 객체를 @Autowired 로 생성하지 않음

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

 

프로젝트 myBatis2

- 현재 프로젝트는 DAO 클래스에서 @Autowired 로 SqlSession 을 주입할 것

- DB연동을 위해서 MyBatis 환경설정 파일들 대신 root-context.xml 에서 DB 접속에 대한 내용을 처리

- 내용은 프로젝트 myBaits1 과 연결된 내용

- 현재 프로젝트 myBatis2 프로젝트에서는 DB 연동 관련 내용을 Spring에서 제어, Spring 에서 DB 연동할 것


MyBatis 연동해서 Oracle DB 연동

 

Spring - MyBatis 연동 예제 2

프로젝트 myBatis2

 

실습 준비

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

- scott 계정 소유 테이블을 사용하므로 따로 테이블을 만들지는 않음

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

 

프로젝트 구조

- DB 연동에 필요한 MyBatis 환경설정 파일들은 있지만 안에 내용이 없음

- 인터페이스와 구현클래스가 1쌍씩 만들어져 있다 ex) Service 인터페이스, Service 구현클래스

- 테이블이 2개이므로 Controller, DAO, Service 가 2개씩 만들어져있다

 

테이블이 늘어나면

1. Controller 클래스가 늘어남

2. DAO 클래스도 늘어남

3. Service 클래스도 늘어남

4. Mapper 파일도 늘어남

- 여기서 테이블을 Dept, Emp 둘 다 사용하므로 Dept 클래스들이 따로, 따로 Emp 클래스들이 따로 만들어짐


파일들 살펴보기 : pom.xml

- 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

- web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
	<!-- 한글 입력 -->
	<filter>
		<filter-name>CharacterEncodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
		<init-param>
			<param-name>forceEncoding</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>CharacterEncodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/spring/root-context.xml</param-value>
	</context-param>
	
	<!-- Creates the Spring Container shared by all Servlets and Filters -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	<!-- Processes application requests -->
	<servlet>
		<servlet-name>appServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>		
	<servlet-mapping>
		<servlet-name>appServlet</servlet-name>
		<url-pattern>*.do</url-pattern>
	</servlet-mapping>
</web-app>

- 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

<?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="myBatis2" />	
	
</beans:beans>

<ViewResolver>

- View 파일들이 저장될 최상위 디렉트리를 지정하고 있다

- 현재는 "/WEB-INF/views" 가 View 파일들의 최상위 디렉토리이다

- 주의 : /WEB-INF/views 아래 어떤 폴더가 있고 그 안에 어떤 View 페이지가 있다면 Controller 에서 View 페이지로 찾아갈때 그 하위 폴더를 경로에 추가해야함

ex) 현재는 views 폴더 아래 emp 폴더가 있고 그 안에 일부 View 파일들이 있다, Controller 클래스에서 그 파일로 갈때는 return "emp/empList.jsp" 로 가야함

<base-package>

- base-package 를 지정한다는 의미는 어노테이션 기반을 사용하겠다는 의미

-base-package는 base 가 되는 자바파일들이 저장될 최상위 디렉토리를 지정하는 것이다, 이 패키지는 java 폴더 하위에 있어야한다

- 이 base-package 안에 클래스들이 있어야함, 그 클래스들 위에는 4가지 어노테이션 중 하나가 붙어있어야만 Service / Controller / DAO 기능 수행 가능

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

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

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

 

+ servlet-context.xml 에서는 Setter DI (ViewResolver) 도 쓰고 있고 어노테이션 기반 DI (base-package) 도 있다

 

+ 원래 Spring 프로젝트 생성시 지정했던 top-level 패키지명 보기

- pom.xml 부분

- com.ch.myBatis2 이었다

- 이 패키지를 지우고 myBatis2 만 남긴것


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

1. jdbc.properties

2, configuration.xml

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

 


파일들 살펴보기 : Configuration.xml

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>

	<typeAliases>
		<typeAlias alias="dept" type="myBatis2.model.Dept" />
		<typeAlias alias="emp"  type="myBatis2.model.Emp" />
	</typeAliases>	
	
	<!-- 
	<mappers>
		<mapper resource="sql/Dept.xml" />
		<mapper resource="sql/Emp.xml" />
	</mappers>  -->	
	
</configuration>

 

- configuration.xml 의 3가지 내용(DTO alias, DB접속 내용, Mapper 파일 호출) 중 DTO alias 설정하는 코드만 남아있다

- DTO alias 설정 내용만 있다

 

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

- Dept.xml, Emp.xml 이 있다

- Mapper 파일들 안에 내용은 모두 그대로 이다

- Spring - MyBatis 연동하고 Spring 으로 DB 연동을 처리하더라도 Mapper 파일의 내용은 같다, 그대로 SQL문들이 있음

- Spring - MyBatis 연동할때 Mapper 파일일은 JSP - MyBatis 연동때 Mapper 파일과 똑같다


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

- 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"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="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-4.1.xsd">
	
	<!-- <context:property-placeholder location="classpath:jdbc.properties" /> -->
	
	<!-- dataSource -->
	<!-- <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
		destroy-method="close">
		<property name="driverClass" value="${jdbc.driverClassName}" />
		<property name="jdbcUrl" value="${jdbc.url}" />
		<property name="user" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
		<property name="maxPoolSize" value="${jdbc.maxPoolSize}" />
	</bean> -->
	
	<!-- Data Source -->
	<bean id="dataSource"
		class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
		<property name="driverClass" value="oracle.jdbc.driver.OracleDriver" />
		<property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" />
		<property name="username" value="scott" />
		<property name="password" value="tiger" />
	</bean>
	
	<!-- 스프링 jdbc 즉 스프링으로 oracle 디비 연결 -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="configLocation" value="classpath:configuration.xml" />
		<property name="mapperLocations" value="classpath:sql/*.xml" />
	</bean>
	
	<bean id="session" class="org.mybatis.spring.SqlSessionTemplate">
		<constructor-arg index="0" ref="sqlSessionFactory" />
	</bean>	
	
</beans>

- DB 연동을 Spring 쪽에서 제어하므로, root-context.xml 안에 DB 연결에 필요한 내용을 처리

- 이전 프로젝트와 다르게 configuration.xml 에는 DTO alias 내용만 남고 나머지 설정은 모두 이 root-context. 가 처리

- root-context.xml 에서는 어노테이션 기반 DI + Setter DI 다 있다

- root-context.xml 에서 beans 가 루트 엘리먼트이고 안에서 3개의 bean 을 만들어서 처리하고 있다

- 3개의 bean 을 만들면서 2번의 Setter DI 와 1번의 Constructor DI 를 하고 있다

 

root-context.xml 부분 1

	<!-- Data Source -->
	<bean id="dataSource"
		class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
		<property name="driverClass" value="oracle.jdbc.driver.OracleDriver" />
		<property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" />
		<property name="username" value="scott" />
		<property name="password" value="tiger" />
	</bean>

- 여러 클래스를 사용 가능하지만 현재는 SimpleDriverDataSource 클래스를 사용, 클래스마다 정해진 Property 들이 달라진다

- Setter DI 를 통해 필드 driverClass, url, username, password 에 value 의 값들을 주입하고 있다

- 오라클 연결 관련 내용(계정명, 비번 등) 들을 주입하고 있다

 

root-context.xml 부분 2

	<!-- 스프링 jdbc 즉 스프링으로 oracle 디비 연결 -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="configLocation" value="classpath:configuration.xml" />
		<property name="mapperLocations" value="classpath:sql/*.xml" />
	</bean>

- SqlSessionFactoryBean 객체 sqlSessionFactory 를 생성하고 있다

- SqlSessionFactoryBean 클래스의 프로퍼티 dataSource, configLocation, mapperLocations 에 값들을 주입하고 있다

 

DB 연결 코드 (root-context.xml 부분 2-1)

		<property name="dataSource" ref="dataSource" />

- dataSource 프로퍼티로 위에서 만든 SimpleDriverDataSource 객체인 dataSource 객체를 주입시킨다

- 이 코드가 DB 를 연결하는 코드이다

 

DB 연결 코드 (root-context.xml 부분 2-2)

		<property name="configLocation" value="classpath:configuration.xml" />

- configLocation : MyBatis 환경설정 파일(Configuration.xml) 의 위치를 설정하는 역할

- MyBatis 환경설정 파일인 configuration.xml 을 불러오고 있다

- MyBatis 환경설정 파일인 configuration.xml 은 resources 패키지 아래에 있으므로 classpath: 를 붙여서 불러온다

+ configuration.xml 에는 3가지 내용(DTO alias, DB접속 내용, Mapper 파일 호출) 중 DTO alias 설정하는 코드만 남아있다

 

DB 연결 코드 (root-context.xml 부분 2-3)

		<property name="mapperLocations" value="classpath:sql/*.xml" />

- mapperLocations : Mapper 파일들의 위치를 설정하는 역할

- Mapper 파일들은 resources 폴더 내에 있으므로 classpath: 를 붙이고 그 안에서도 sql 폴더 안에 있으므로 "classpath:sql/" 를 설정

- 그리고 resources/sql 안의 모든 xml 파일을 불러오라는 의미로 "classpath:sql/*xml"

 

 

root-context.xml 부분 3

	<bean id="session" class="org.mybatis.spring.SqlSessionTemplate">
		<constructor-arg index="0" ref="sqlSessionFactory" />
	</bean>

- SqlSessionTemplate 클래스는 SQL 5개 메소드를 제공하는 myBatis 에서 지원하는 구현클래스이다

- 인터페이스 SqlSession 로는 객체를 생성할 수 없으므로 구현 클래스인 SqlSessionTemplate 으로 객체를 생성해야한다

+ SqlSessionTemplate 객체를 생성했으므로 그걸 업캐스팅해서 SqlSession에 주입해도 되고 SqlSessionTemplate 에 주입해도 된다

- 이 코드로 SqlSessionTemplate 객체를 생성하고 있다, 이 객체는 자동으로 DAO 의 @Autowired 가 붙은 SqlSessionTemplate 또는 SqlSession 으로 주입된다

- 이 코드로 이렇게 SqlSessionTemplate 객체를 생성해야만 DAO 클래스 안에서 @Autowired 로 SqlSessionTemplate 또는 SqlSesssion 으로 객체 주입 가능하다

+ 이 코드가 없어도 Service 객체는 Controller 로 , DAO 객체는 Service 로 주입된다, 단 SqlSession 객체를 DAO 에 주입할때는 반드시 이 코드가 있어야함

- Constructor DI 를 하고 있다, 여기가 유일하게 Constructor DI 를 하는 곳이다

- 즉, 생성자의 매개변수로 주입한다, index = "0" 은 생성자 매개변수 중 첫번째 매개변수를 의미함, 그 생성자의 첫번째 매개변수에 위에서 생성된 sqlSessionFactory 객체를 주입함

- 생성자를 통해 SqlSessionTemplate 객체의 프로퍼티를 채우고, 그렇게 생성된 SqlSessionTemplate 객체를 DAO 의 필드로 주입하는 것임

 

+ 이러한 내용을 프로젝트 myBatis1 에서는 Configuration.xml 과 DAO 에서 처리했다

- Configuration.xml 에서 DB 연결하고 Mapper 파일을 불러왔다

- DAO 에서 Configuration.xml 을 읽어오고 SqlSession 객체를 생성했었다


-프로젝트 myBatis1 (MyBatis 에서 DB연동) 의 내용을 잠시 보자

프로젝트 myBatis1 의 Configuration.xml 

- 여기서 DB 연결과 Mapper 파일을 불러왔었다

- 그리고 DAO 에서는 이 Configuration.xml 을 읽어왔다

- 즉 흐름은 아래와 같았다


- 현재 프로젝트 myBatis2 에서 DB 연동하는 걸 보자

- 위와 비교

 

DB 접속 내용

- root-context.xml 에서 SimpleDriverDataSource 객체를 생성할때 필드값으로 들어간다

- 이 SimpleDriverDataSource 객체를 SqlSessionFactoryBean 객체를 생성할때 주입함

 

Mapper 파일을 불러오는 방법 (DAO 에서 Mapper 파일에 접근 가능한 이유)

- SqlSessionFactoryBean 객체에는 Mapper 파일의 위치가 들어간다

- SqlSessionFactoryBean 객체인 sqlSessionFactory 가 SqlSessionTemplate 객체를 생성할때 ref 로 주입되었기 떄문에 DAO 에서 Mapper 파일을 불러와서 id 로 SQL문에 접근 가능한 것

- 현재 프로젝트 myBatis2 에서는 root-context.xml 을 읽어서 SqlSession 객체를 생성한다


파일들 살펴보기 : DeptDaoImpl.java

- DAO 구현 클래스 중 하나를 살펴보자

- DeptDaoImpl.java

package myBatis2.dao;

import java.util.List;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import myBatis2.model.Dept;

@Repository
public class DeptDaoImpl implements DeptDao {
	@Autowired
	private SqlSessionTemplate st;

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

	public int insert(Dept dept) {
		return st.insert("deptns.insert", dept);
	}

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

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

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

- @Autowired 어노테이션을 사용해서 SqlSession 객체를 주입하고 있다

- 인터페이스인 SqlSession 을 주입해도 되고, 그걸 상속받는 구현 클래스인 SqlSessionTemplate 를 주입해도 된다

- SqlSession 은 SQL문을 실행시키는 메소드를 제공하는 인터페이스

- 여기선 구현클래스인 SqlSessionTemplate 으로 객체를 주입하고 있다

- 주입되어야만 5개의 메소드(insert(), update(), delete(), selectOne(), selectList() 를 사용 가능함

- root-context.xml 에서 DB 연동을 처리했기 때문에 여기서 @Autowired 로 SqlSession 객체를 주입 가능한 것

- root-context.xml 에서 SqlSessionTemplate 객체를 생성했고 어노테이션 기반 DI 로 여기에 주입했다

 

프로젝트 myBaits1 vs 프로젝트 myBatis2

프로젝트 myBaits1

- 이전 프로젝트 myBatis1은 여기서 MyBatis 환경설정 파일을 읽어와서 SqlSession 객체를 생성했다

- 이전 프로젝트에서는 MyBatis 환경설정 파일을 썼으므므로 @Autowired 로 SqlSession 주입 불가

 

프로젝트 myBatis2

- 현재 프로젝트 myBatis2는 여기서 @Autowired 어노테이션으로 SqlSession 을 주입받았다

- root-context.xml 에서 DB 연동을 처리했기 때문에 여기서 @Autowired 로 SqlSession 객체를 주입 가능한 것

- 그 root-context.xml 에서 객체 생성 및 주입을 했고 시점은 root-context.xml 이 web.xml 에 의해 읽어졌을때


파일들 살펴보기 : DeptServiceImpl.java

- Service 클래스 중 하나를 보자

- DeptServiceImpl.java

package myBatis2.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import myBatis2.dao.DeptDao;
import myBatis2.model.Dept;

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

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

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

	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);
	}
}

- DAO 인터페이스 DeptDao 로 객체를 주입하고 있다

- @Autowired 로 객체를 주입할때 인터페이스로 객체를 주입해도되고, 구현클래스로 주입해도 된다

+ 단 어노테이션을 붙일때는 구현클래스 위에만 붙인다


파일들 살펴보기 : EmpController.java

- Controller 클래스 중 하나를 보자

- EmpController.java

package myBatis2.controller;

import java.sql.Date;
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 myBatis2.model.Dept;
import myBatis2.model.Emp;
import myBatis2.service.DeptService;
import myBatis2.service.EmpService;

@Controller
public class EmpController {
	@Autowired
	private EmpService es;
	@Autowired
	private DeptService ds;

	@RequestMapping("empList.do")
	public String empList(int deptno, Model model) {
		Dept dept = ds.select(deptno);
		List<Emp> list = es.list(deptno);
		model.addAttribute("dept", dept);
		model.addAttribute("list", list);
		return "emp/empList";
	}

	@RequestMapping("empInsertForm.do")
	public String empInsertForm(Model model) {
		List<Dept> deptList = ds.list();
		List<Emp> empList = es.empList();
		model.addAttribute("deptList", deptList);
		model.addAttribute("empList", empList);
		return "emp/empInsertForm";
	}

	@RequestMapping("dupCheck.do")
	public String dupCheck(int empno, Model model) {
		System.out.println("empno:"+empno);
		Emp emp = es.select(empno);
		String msg = "";
		if (emp != null)
			msg = "이미 있는 데이터입니다";
		else
			msg = "사용 가능한 사번 입니다";
		model.addAttribute("msg", msg);
		return "emp/dupCheck";
	}

	@RequestMapping("empInsert.do")
//	public String empInsert(Emp emp, String hiredate1, Model model) {
	public String empInsert(Emp emp, Model model) {
//		emp.setHiredate(Date.valueOf(hiredate1)); // String -> Date 형변환
		int result = es.insert(emp);
		model.addAttribute("result", result);
		model.addAttribute("deptno", emp.getDeptno());
		return "emp/empInsert";
	}

	@RequestMapping("empView.do")
	public String empView(int empno, Model model) {
		Emp emp = es.select(empno);
		model.addAttribute("emp", emp);
		return "emp/empView";
	}

	@RequestMapping("empDelete.do")
	public String empDelete(int empno, Model model) {
		Emp emp = es.select(empno);
		int result = es.delete(empno);
		model.addAttribute("result", result);
		model.addAttribute("deptno", emp.getDeptno());
		return "emp/empDelete";
	}

	@RequestMapping("empUpdateForm.do")
	public String empUpdateForm(int empno, Model model) {
		Emp emp = es.select(empno);
		List<Dept> deptList = ds.list();
		model.addAttribute("emp", emp);
		model.addAttribute("deptList", deptList);
		return "emp/empUpdateForm";
	}

	@RequestMapping("empUpdate.do")
	public String empUpdate(Emp emp, Model model) {
		int result = es.update(emp);
		model.addAttribute("deptno", emp.getDeptno());
		model.addAttribute("result", result);
		return "emp/empUpdate";
	}

	@RequestMapping("empAllList.do")
	public String empAllList(Model model) {
		List<Emp> list = es.empAllList();
		model.addAttribute("list", list);
		return "emp/empAllList";
	}
}

- Service 객체를 2개 주입받고 있음

- @Autowired 로 주입 2개 이상도 가능하다


흐름 보기

- @Autowired 로 객체를 주입할때 인터페이스로 객체를 주입해도되고, 구현클래스로 주입해도 된다

- 여기서는 다 인터페이스로 주입하고 있음 ex) BoardService, BoardDao, SqlSession

 

필요한 bean 객체를 어노테이션 기반으로 주입하기 위한 조건

1. base-package 에 적힌 해당 패키지의 모든 클래스를 읽어오는 코드가 있어야함

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

3. 만들고자 하는 객체 위에 @Autowired 어노테이션을 붙여야함


프로젝트 실행

- index.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>
<script type="text/javascript">
	location.href="deptList.do";
</script>
</body>
</html>

 

전체 직원 목록

- 사번, 이름, 업무, 급여, 부서코드는 Emp 테이블에 있는 내용이다

- 부서모드, 부서명, 근무지는 Dept 테이블에 있는 내용이다

- 검색할때 조인해서 검색해서 그걸 뿌려준 것이다

 

부서 목록

- 현재는 삭제가 되지 않음, Emp 테이블에서 참조하는 Foreign Key 가 없을때는 삭제됨

- 그 외, 부서 입력(추가), 사원 입력(추가) 도 가능하다

 

조인(join) 과 DTO

- 조인한 결과를 DTO 객체를 생성하여 저장해야한다

- 현재 DTO 클래스는 Dept 와 Emp 가 있다

+ 테이블 개수가 늘어나면 Mapper 파일, Controller , Service , DAO , DTO 도 1개씩 늘어나는게 일반적

- Dept 테이블에 대해 매핑을 잡을때를 위해 Dept DTO의 필드명은 Dept 테이블의 컬럼명과 일치시켰다

- Emp 테이블에는 8 개의 컬럼이 있지만 Emp DTO 의 필드는 8개보다 많다, 조인을 했을때 조인한 결과를 이 DTO 객체에 매핑을 통해서 저장할 것

- deptno, dname, loc 는 Emp 테이블에는 없는 컬럼이다, 조인을 했을때 그 결과를 저장하기 위해 만든 컬럼이다

 

+ Configuration.xml 에서 Dept DTO 의 alias 값을 "dept", Emp DTO 의 alias 값을 "emp" 로 설정했다

 

- 이제 실행되는 순서대로 따라가보자



프로젝트 myBatis2 : 부서 목록

+ 전체 코드는 나중에 한번에 올림

- index.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>
<script type="text/javascript">
	location.href="deptList.do";
</script>
</body>
</html>

- "deptList.do" 로 요청한다, DispatcherServlet -> Controller 로 이동

- 2개의 Controller 클래스가 있지만 @RequestMapping 이 맞는 곳으로 찾아가므로 신경쓰지 않아도 된다

 

Controller 클래스 DeptController.java 에서 "deptList.do" 요청 부분만 + @Autowired 부분

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

- @Autowired 를 통해서 DeptServiceImpl 객체를 인터페이스인 DeptService ds 에 주입

+ 엄밀히 말하면 구현클래스가 주입되는 것이지만, 어차피 업캐스팅 하므로 그냥 DeptService 객체가 됨

+ 그래서 하나의 인터페이스에 구현클래스를 2개를 만들면 안된다

- 위에서 주입한 Service 객체 ds 를 사용해서 Service 의 list() 를 호출

- 2개이상의 데이터를 검색해서 List 로 돌아올 것이므로 리턴을 List 로 받는다

<돌아온 후>

- Dept DTO 객체를 저장하고 있는 리스트를 반환받았고 그걸 list 에 저장함

- 그 list 를 Model 객체에 저장하고, /WEB-INF/views/deptList.jsp 로 간다

+ Model 에 리스트가 저장되었으므로 View 페이지에서 출력할때는 EL 이 forEach문의 items 에 들어감

 

Service 클래스 DeptServiceImpl.java 에서 list() 메소드 부분만 + @Autowired 부분

	@Autowired
	private DeptDao dd;

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

- @Autowired 를 통해서 DeptDaoImpl 객체를 인터페이스인 DeptDao dd 에 주입

+ 엄밀히 말하면 구현클래스가 주입되는 것이지만, 어차피 업캐스팅 하므로 그냥 DeptDao 객체가 됨

+ 그래서 하나의 인터페이스에 구현클래스를 2개를 만들면 안된다

- 돌려받은 값(리스트)을 그대로 리턴

 

DAO 클래스 DeptDaoImpl.java 에서 list() 메소드 부분만 + @Autowired 부분

	@Autowired
	private SqlSessionTemplate st;

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

- @Autowired 를 통해서 SqlSessionTemplate 객체를 SqlSessionTemplate st 에 주입

- 2개 이상의 데이터를 검색할 것이므로 selectList() 를 호출

- 이 selectList() 메소드는 위에서 SqlSessionTemplate 객체를 구해왔으므로 사용 가능한 것

- 현재는 Mapper 파일이 2개이므로 id 중복을 피하기 위해 namespace 를 사용한다

+ Mapper 파일 중 하나인 Dept.xml 의 namespace 는 "deptns"

- 그래서 selectList("deptns.list") 는 Dept.xml 파일에서 id 가 list 인 태그의 select SQL문을 불러오는 것을 의미

- 돌려받은 값(리스트)을 그대로 리턴

 

Mapper 파일 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>

- Dept 테이블의 모든 데이터를 검색

- order by 로 부서번호 기준 오름차순 정렬되어서 돌려준다 

- 현재는 Dept DTO 클래스 프로퍼티명과 검색되는 컬럼명인 Dept 테이블의 컬럼명이 일치하므로 resultMap 을 사용하지 않아도 된다, 사용은 하고 있음

- resultMap 에서 type 은 "dept" 이다, 즉 Dept DTO 타입,

+ 리스트로 반환할때도 type 이나 returyType 에는 DTO 를 써야함

 

- 이제 다시 DAO 로 돌아가야한다, 돌아갈때의 설명은 하늘색 하이라이트로 표시

 

- 돌아갔다가 이제 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" align="center">
		<h2 class="text-primary">부서 목록</h2>
		<table class="table table-hover">
			<tr>
				<th>부서코드</th>
				<th>부서명</th>
				<th>근무지</th>
				<th></th>
				<th></th>
			</tr>
			<c:if test="${empty list}">
				<tr>
					<td colspan="5">데이터가 없습니다</td>
				</tr>
			</c:if>
			<c:if test="${not empty list }">
				<c:forEach var="dept" items="${list}">
					<tr>
						<td>${dept.deptno}</td>
						<td><a href="empList.do?deptno=${dept.deptno}"
							   class="btn btn-info">${dept.dname}</a></td>
						<td>${dept.loc }</td>
						<td><a href="deptUpdateForm.do?deptno=${dept.deptno }"
							   class="btn btn-success">수정</a></td>
						<td><a href="deptDelete.do?deptno=${dept.deptno }"
							   class="btn btn-danger">삭제</a></td>
				</c:forEach>
			</c:if>
		</table>
		<a href="deptInsertForm.do" class="btn btn-info">부서입력</a> 
		<a href="empAllList.do" class="btn btn-default">전체 직원목록</a>
	</div>
</body>
</html>

 

- list 가 비어있는 경우는 ${empty list} 로 확인하고, "데이터가 없습니다" 뿌려주기

- list 가 비어있지 않는 경우는 Model 에 저장되어있는 걸 EL 로 가져와서 부서 목록을 출력해준다

- forEach 문의 itmes 에 ${list} 가 들어가게 되고, 반복문으로 하나씩 Dept DTO 객체를 가져와서 뿌려준다

 

- '전체 직원 목록' 클릭시 "empAllList.do" 로 요청한다


프로젝트 myBatis2 : 전체 직원 목록

+ 전체 코드는 나중에 한번에 올림

- '전체 직원 목록' 클릭시

- Dept, Emp 테이블을 등가조인해서 결과를 한번에 출력하고 있다

- '전체 직원 목록' 클릭시 "empAllList.do" 로 요청한다

 

EmpController.java 에서 @Autowired 부분만

	@Autowired
	private EmpService es;
	@Autowired
	private DeptService ds;

- 주입할 객체가 여러개인 경우엔 @Autowired 를 개별적으로 써야한다, 1번만 써선 안된다

- DeptService 객체를 주입하고 있고, EmpService 객체도 주입하고 있다

+ 엄밀히 말하면 구현클래스가 주입되는 것이지만, 어차피 업캐스팅 하므로 그냥 DeptService 객체, EmpService 객체가 됨

+ 그래서 하나의 인터페이스에 구현클래스를 2개를 만들면 안된다

- 2개의 Service 객체를 주입받음

 

Controller 클래스 EmpController.java 에서 "empAllList.do" 요청 부분만

	// 전체 직원 목록
	@RequestMapping("empAllList.do")
	public String empAllList(Model model) {
		List<Emp> list = es.empAllList();
		model.addAttribute("list", list);
		return "emp/empAllList";
	}

- 위에서 주입받은 EmpService 객체 es 를 사용해서 Service 클래스 메소드인 empAllList() 호출

- 2개 이상의 데이터를 검색하므로 리스트로 값이 돌아올 것이므로 List 로 받음

<돌아올 때>

- 돌아온 List 에는 Emp DAO 객체들이 들어있다

- prefix 로 잡혀있는 경로까지는 생략하고, 그 하위의 경로는 적어야한다

- /WEB-INF/views 는 생략하고, 하위의 emp 폴더는 적어야함, 그래서 "emp/empAllList" 이다

- /WEB-INF/views/emp/empAllList.jsp 파일로 이동

 

Service 클래스 EmpServiceImpl.java 에서 empAllList() 메소드 부분만 + @Autowired 부분

	@Autowired
	private EmpDao ed;
	public List<Emp> empAllList() {
		return ed.empAllList();
	}

- 위에서 @Autowired 로 주입받은 EmpDao 객체 ed 를 사용해서 DAO 클래스의 메소드인 empAllList() 호출

 

DAO 클래스 EmpDaoImpl.java 에서 empAllList() 메소드 부분만 + @Autowired 부분

	@Autowired
	private SqlSessionTemplate sst;
	public List<Emp> empAllList() {
		return sst.selectList("empAllList");
	}

- 위에서 @Autowired 로 주입받은 SqlSEssionTemplate 객체 sst 를 사용해서 지원메소드인 selectList() 사용

- 2개 이상의 데이터를 검색하므로 selectList() 메소드 사용

- 반환되는 List 에 들어가는 것은 Emp DTO 객체만 들어간다는 의미로 제네릭 <Emp> 사용

 

Mapper 파일 Emp.xml 에서 id 가 "empAllList" 인 태그 부분만 + resultMap

	<!-- Use type aliases to avoid typing the full classname every time. -->
	<resultMap id="empResult"    	type="emp">
		<result property="empno" 	column="empno" />
		<result property="ename"  	column="ename" />
		<result property="job"		column="job" />
		<result property="mgr" 		column="mgr" />
		<result property="hiredate" column="hiredate" />
		<result property="sal"	  	column="sal" />
		<result property="comm"	   	column="comm" />
		<result property="deptno"   column="deptno" />
		<result property="dname"	column="dname" />
		<result property="loc"   	column="loc" />
	</resultMap>

+ 현재는 모든 10개의 테이블과 검색결과의 컬럼명이 일치하므로 일일히 resultMap 을 사용할 필요 없다, 여기선 resultMap 을 사용했음

- Emp DTO 가 저장된 리스트를 리턴하므로, type 을 "emp" 로 설정함

	<select id="empAllList" resultMap="empResult">
		select e.*,dname,loc from emp e, dept d 
			where e.deptno=d.deptno order by empno
	</select>

- from 절에서 emp 테이블의 별칭을 e, dept 테이블의 별칭을 d 로 설정

- 등가조인(과거부터 사용하던 방식, 안시 방식 등이 있다) 을 사용하고 있음

- e 의 모든 컬럼과 dname, loc 를 검색

+ 공통 컬럼이 아닌 경우는 dname, loc 처럼 그냥 쓰면 된다 

- 등가 조인을 위해 e.deptno 와 d.deptno 가 같은 경우만 가져옴

+ 두 테이블 사이 공통적인 컬럼이 있을때만 가능한게 등가조인

- 사원번호 순서대로 정렬, 사원번호가 빠른(작은) 순대로 나온다

- Emp DTO 는 10개의 정보를 저장하므로 검색결과를 모두 저장할 수 있으므로 resultMap 을 쓰지 않고 resultType 에 "emp" 를 써도 된다

 

View 페이지 empAllList.jsp

- emp/empAllList.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">
		<table class="table table-striped">
			<tr>
				<td>사번</td>
				<td>이름</td>
				<td>업무</td>
				<td>급여</td>
				<td>부서코드</td>
				<td>부서명</td>
				<td>근무지</td>
			</tr>
			<c:forEach var="emp" items="${list }">
				<tr>
					<td>${emp.empno }</td>
					<td>${emp.ename }</td>
					<td>${emp.job }</td>
					<td>${emp.sal }</td>
					<td>${emp.deptno }</td>
					<td>${emp.dname }</td>
					<td>${emp.loc }</td>
				</tr>
			</c:forEach>
		</table>
		<a href="deptList.do" class="btn btn-default">부서목록</a>
	</div>
</body>
</html>

- 검색된 결과가 Model 을 통해 넘어왔다, 이 값은 Emp DTO 객체를 저장하는 리스트이다

- 리스트가 Model 객체에 저장되었으므로 forEach 태그의 items 값으로 들어간다

- 각각의 Emp DTO 객체는 10개의 값을 저장하고 있음, 그 값들 중 일부를 출력한다

- 또한 forEach 를 통해 하나씩 가져온 각각의 DTO 객체의 필드에 접근할땐 ${emp.필드명} 를 사용함

+ ${emp.empno} 는 내부적으로는 ${emp.getEmpno()} 이다

- 사번, 이름, 업무, 급여, 부서코드는 Emp 테이블, 부서코드, 부서명, 근무지는 Dept 테이블의 데이터

 

- 아래의 '부서목록' 클릭시 "deptlist.do" 로 요청한다  (위에서 설명했다)

- index.jsp 에서 요청했던 내용과 같음

 

- 사원 14명에 대한 정보를 출력한다


프로젝트 myBatis2 : 부서 입력폼

+ 전체 코드는 나중에 한번에 올림

- 입력폼으로 먼저 이동한 후, 거기서 입력을 해야한다, 입력 폼만 먼저 보자

 

- deptList.jsp (부서 목록) 에서 '부서입력' 클릭시 "deptInsertForm.do" 로 요청한다

		<a href="deptInsertForm.do" class="btn btn-info">부서입력</a>

- 부서 입력 폼으로 가게 됨

 

Controller 클래스 DeptController.java 에서 "deptInsertForm.do" 요청 부분만

	// 부서 등록폼
	@RequestMapping("deptInsertForm.do")
	public String deptInsertForm() {
		return "deptInsertForm";
	}

- 입력폼으로 가는데는 DB 연동이 필요하지 않음, Service 클래스가 아닌 바로 View 페이지로 간다

- 바로 View 파일인 /WEB-INF/views/deptInsertForm.jsp 로 이동한다

 

View 페이지 deptInsertForm.jsp

- deptInsertForm.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="deptInsert.do" method="post">
			<table class="table table-hover">
				<tr>
					<td>부서코드</td>
					<td><input type="number" maxlength="2" name="deptno"
						required="required" autofocus="autofocus"></td>
				</tr>
				<tr>
					<td>부서명</td>
					<td><input type="text" name="dname" required="required"></td>
				</tr>
				<tr>
					<td>근무지</td>
					<td><input type="text" name="loc" required="required"></td>
				</tr>
				<tr>
					<td colspan="2"><input type="submit" value="확인"></td>
				</tr>
			</table>
		</form>
	</div>
</body>
</html>

- 부서 입력 폼에서 입력을 한 후 "확인" 을 누르면 "deptInsert.do" 로 요청한다

- "deptInsert.do" 는 실제 입력(insert) 을 위한 요청임

- 각 입력양식의 name 값은 Dept DTO 의 컬럼명과 일치시켰다, "deptInsert.do" 요청을 받는 곳에서 Setter 메소드 직접 사용 없이 바로 DTO 객체에 넘어온 값들이 저장되도록 하기 위해서이다

 

- 부서 입력 폼으로 가보자

- 부서 입력 폼에서 입력을 한 후 "확인" 을 누르면 "deptInsert.do" 로 요청한다

 


프로젝트 myBatis2 : 부서 입력 (부서 추가 등록)

- 부서 입력 폼에서 입력을 한 후 "확인" 을 누르면 "deptInsert.do" 로 요청한다

 

Controller 클래스 DeptController.java 에서 "deptInsert.do" 요청 부분만

	// 부서 등록
	@RequestMapping("deptInsert.do")
	public String deptInsert(@ModelAttribute Dept dept, 
			                 Model model) {
		Dept dt = ds.select(dept.getDeptno()); // 부서번호 중복 검사
		if (dt == null) { // 부서번호가 중복이 아닌 경우
			int result = ds.insert(dept);
			model.addAttribute("result", result);
		} else { // 부서번호 중복
			model.addAttribute("msg", "이미 있는 데이터입니다");
			model.addAttribute("result", -1);
		}
		return "deptInsert";
	}

- 앞의 부서 입력폼에서 넘어온 3가지값을 @ModelAttribute 로 한번에 받아서 Dept DTO 객체 dept 에 저장함

- 부서번호가 중복되지 않을때만 가입을 시키기 위해 부서번호가 중복인지를 중복검사를 검색해야함

<부서번호 중복 검사>

- 위에서 주입된 Dept Service 객체 ds를 사용해서 Service 클래스의 select() 를 호출, 호출하며 부서번호를 넘겨준다 *아래에

<select() 에서 돌아왔을 때>

- 부서 번호로 DB 에 검색했을때 돌아오는 값이 없어야 부서번호가 중복이 아닌 경우이다 그래서 dt == null 을 확인

<부서 입력>

- dt == null 인 경우, 즉 중복이 아닌 경우에는 위에서 주입된 Dept Service 객체 ds를 사용해서 Service 클래스의 insert() 를 호출

- insert() 를 호출하며 사용자가 부서 입력 폼에서 입력했던 데이터를 저장한 객체 dept 를 넘겨준다

<insert() 에서 돌아왔을 때>

- dt == null 인 경우, 즉 중복이 아닌 경우 insert()를 수행 후 돌아오면, result 에 1 을 받게 된다, Model 객체에 "result" (1) 을 저장해서 detpInsert.jsp 로 이동

- dt != null 인 경우, 즉 중복인 경우에는 Model 객체에 "msg" 와 "result"(-1) 을 저장해서 deptInsert.jsp 로 이동

- 입력에 성공했는지 확인하기 위해 리턴값을 result 변수로 받는다, 그 값을 Model 객체에 저장해서 View 에 넘김

+ 그럼 deptInsert.jsp 에서는 "result" 값을 확인해서 조건식을 통해 성공과 실패 처리를 한다


<부서번호 중복 검사>

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

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

 

 

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

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

- 매개변수로 받은 부서번호 deptno 를 넘겨준다

 

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

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

- 전달받은 부서번호를 통해 (#{deptno}) 데이터를 검색한다

- 이때 검색되는 데이터가 있으면 부서번호가 중복되는 것임

- 리턴 자료형은 Dept DTO 객체인 dept


<부서 입력>

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

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

- 전달받은 insert 할 Dept DTO 객체 dept 를 DAO의 insert() 를 호출할때 매개변수로 전달한다

 

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

	public int insert(Dept dept) {
		return st.insert("deptns.insert", dept);
	}

- 위에서 주입받은 SqlSessionTemplate 객체 st 를 사용해서 insert() 메소드 사용

- insert 할 데이터를 저장한 객체 dept 를 전달함

 

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

	<insert id="insert" parameterType="dept">
		insert into dept values(#{deptno},#{dname},#{loc})
	</insert>

- 넘어온 객체 dept 에서 부서코드,부서명,지역 을 가져와서 DB의 dept 테이블에 삽입한다

- 넘어온 값이 객체이므로 그 ${객체의필드명} 으로 값을 가져온다

ex) #{deptno} 는 dept.getDeptno() 와 같은 의미


View 페이지인 deptInsert.jsp

- deptInsert.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>
	<c:if test="${result == -1 }">
		<script type="text/javascript">
			alert("${msg}");
			history.go(-1);
		</script>
	</c:if>
</body>
</html>

- 부서번호가 중복된 경우 result 는 -1 이 되므로,  msg 를 출력하고 이전 페이지인 입력폼으로 돌아간다

- 부서 등록에 성공한 경우 result 는 1 이 되므로, "입력 성공" 을 출력하고 목록 페이지로 가기 위해 "deptList.do" 요청


- 부서를 새로 등록해보자

- 이때, 부서코드는 Primary Key 이므로 중복되선 안됨, 부서 입력시 10,20,30,40 을 부서코드로 쓰면 안된다

 

- 중복된 부서코드 입력시

 

- 중복되지 않은 부서코드 입력시

 


프로젝트 myBatis2 : 부서 정보 수정폼

- 부서 정보 수정을 위해선 먼저 수정폼으로 가야한다

- 부서 정보 수정폼에서는 부서의 상세 정보를 구해서 뿌려줘야함

- deptList.jsp (목록 페이지) 에서 "수정" 을 클릭해보자

<td><a href="deptUpdateForm.do?deptno=${dept.deptno }"
			class="btn btn-success">수정</a></td>

- "수정" 클릭시 "deptUpdateForm.do" 로 요청한다

- GET 방식으로 해당 부서의 부서코드 deptno 를 가져간다

 

Controller 클래스 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";
	}

- 앞에서 넘어온 네임값과 이름이 같은 변수 deptno 에 앞에서 넘어온 부서코드가 바로 저장됨

- @RequestParam("deptno") 가 int deptno 앞에 생략되어있다

- Service 객체 ds 를 사용해서 Service 클래스의 select() 메소드 호출, 호출 시 부서번호를 전달

<돌아온 후>

- select() 에서 돌려받은 Dept DTO 객체 dept 를 Model 객체에 저장

+ 객체 dept 가 Model 에 저장되었으므로 View 에서 출력할땐 ${dept.필드명} 으로 출력

- /WEB-INF/views/deptUpdateForm.jsp 로 간다

 

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

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

 

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

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

- 하나의 부서에 대한 상세정보를 검색해서 가져오므로 selectOne() 메소드 사용

 

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

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

- 검색되는 결과는 1개이다

- 결과는 Dept DTO 형으로 돌려준다, 그래서 resultType 에 "dept" 를 썼다

 

View 페이지 deptUpdateForm.jsp

- 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-hover">
				<tr>
					<td>부서코드</td>
					<td>${dept.deptno}</td>
				</tr>
				<tr>
					<td>부서명</td>
					<td><input type="text" name="dname" required="required"
							   value="${dept.dname}"></td>
				</tr>
				<tr>
					<td>근무지</td>
					<td><input type="text" name="loc" required="required"
							   value="${dept.loc}"></td>
				</tr>
				<tr>
					<td colspan="2"><input type="submit" value="확인"></td>
				</tr>
			</table>
		</form>
	</div>
</body>
</html>

- 객체 dept 가 Model 에 저장되었으므로 View 에서는 ${dept.필드명} 으로 가져온다

- 부서 정보 수정폼에 수정할 정보를 입력하고 "확인" 을 누르면 "deptUpdate.do" 로 요청한다

- 부서코드는 수정할 수 없다, 수정할 수 있는 정보는 부서명과 근무지이다.

+ 현재 부서코드는 입력양식이 아니라 출력만 하고 있으므로 넘어가지 않음, 그래서 hidden 객체 사용

- 부서코드 또한 수정시에 전달해야하므로 hiddden 객체를 통해 "deptUpdate.do" 로 전달

 


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

- 부서 정보 수정폼 deptUpdateForm.jsp 에 수정할 정보를 입력하고 "확인" 을 누르면 "deptUpdate.do" 로 요청한다

 

Controller 클래스 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";
	}

- 앞에서 폼을 통해 넘어온 입력값들을 한번에 @ModelAttribuate (생략) 을 통해 Dept DTO 객체 dept 에 세팅한다

- Service 클래스의 객체 ds 를 사용해서 Service 의 update() 메소드 호출, 호출 시 수정할 정보를 저장한 객체 dept 를 전달

<돌아온 후>

- 수정이 성공적으로 완료되면 update() 에서 1 을 받게 되고, 그걸 result 변수에 저장

- 그 result 변수를 Model 에 저장하고 deptUpdate.jsp 로 이동

 

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

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

 

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

 

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

- update() 수행 후 DB 에서 수정을 성공하면, update() 는 1 을 자동으로 리턴한다 (수정한 데이터 개수)

- Mapper 파일의 SQL문을 가져오는 것이고, 실제 SQL문의 실행은 여기 DAO의 update() 문이 실행한다

- 그 1 을 다시 Service 로 리턴함

 

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

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

- 수정할 정보를 저장한 Dept DTO 객체가 전달된다

- #{dname} 은 dept.getDname() 과 같은 의미

 

View 페이지 deptUpdate.jsp

- 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" 값을 사용해서 성공 / 실패 처리를 한다


 

 

+ 삭제로 넘어가기전 상세 페이지 잠시 보기

- 부서 목록 페이지에서 부서명을 클릭하면 상세 페이지에서 해당 부서 소속 사원들을 보여준다

- 상세페이지는 나중에 구현할 것

- 즉 20번 부서를 참조하는 사원 테이블의 데이터들이 있으므로 20번 부서가 삭제되지 않음

- 20번 부서 삭제 시도시


프로젝트 myBatis2 : 부서 삭제

- deptList.jsp (목록 페이지) 에서 부서 삭제를 해보자

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

- "삭제" 를 누르면 "deptDelete.do" 로 요청한다, 그때 부서 코드인 deptno 를 GET 방식으로 전달한다

 

삭제시 주의

- Emp 테이블과 Dept 테이블은 참조관계가 있다

- 현재 내가 입력한 50번 부서는 참조하는 Foreign Key 가 없으므로 부서 삭제가 된다

- 10,20,30 번 부서는 참조하는 Foreign Key 가 있으므로 삭제되지 않는다

- 10,20,30 번 부서는 사원 테이블 Emp 에서 참조한다

+ 40번 부서는 원래 참조하는 사원이 없으므로 삭제 된다

 

- 20 번 부서 삭제 시도시

 

Controller 클래스 DeptController.java 에서 "deptDelete.do" 요청 부분만

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

- 앞에서 넘어온 네임값과 이름이 같은 변수 deptno 에 앞에서 넘어온 부서코드가 바로 저장됨

- @RequestParam("deptno") 가 int deptno 앞에 생략되어있다

 

<돌아온 후>

- 삭제가 성공적으로 완료되었다면 delete() 메소드는 1 을 리턴함, 그 값을 result 변수에 저장

- result 변수를 Model 객체에 저장하고 deptDelete.jsp 로 이동

 

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

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

 

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

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

- delete() 수행 후 DB 에서 수정을 성공하면, delete() 는 1 을 자동으로 리턴한다 삭제한 데이터 개수)

- Mapper 파일의 SQL문을 가져오는 것이고, 실제 SQL문의 실행은 여기 DAO의 delete() 문이 실행한다

- 그 1 을 다시 Service 로 리턴함

 

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

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

- 넘어온 값은 부서코드이다, #{deptno} 로 사용함

- 해당 부서코드를 가진 부서를 dept 테이블에서 삭제

 

View 페이지 deptDelete.jsp

- 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 <= 0 }">
		<script type="text/javascript">
			alert("삭제 실패");
			location.href = "deptList.do";
		</script>
	</c:if>
</body>
</html>

- "result" 를 통해 삭제 성공 / 실패 처리를 한다

- 삭제 성공시 result 가 1 이므로, "삭제 성공" 을 출력하고, 목록 페이지로 가기 위해 deptList.do 로 요청

- 삭제 실패시 result 가 <=0 이므로 "삭제 실패" 를 출력하고, 목록 페이지로 가기 위해 deptList.do 로 요청

+ 목록 페이지에서 "삭제" 를 눌렀으므로 삭제 실패 후 돌아가는 것도 목록 페이지로 했다

 

- 참조하는 직원이 없는 50 번 부서를 삭제해보자

- 참조하는 직원(자식) 이 없으므로 지워짐

+ 삭제 후 다시 50번 부서를 등록하자(실습 위해)


 

문제

삭제시 주의 (중복)

- Emp 테이블과 Dept 테이블은 참조관계가 있다

- 현재 내가 입력한 50번 부서는 참조하는 Foreign Key 가 없으므로 부서 삭제가 된다

- 10,20,30 번 부서는 참조하는 Foreign Key 가 있으므로 삭제되지 않는다

- 10,20,30 번 부서는 사원 테이블 Emp 에서 참조한다

+ 40번 부서는 원래 참조하는 사원이 없으므로 삭제 된다

 

- 20 번 부서 삭제 시도시

 

문제 해결 방법

참조하는 자식이 있는 부모도 무조건 삭제하려면?

- 테이블 생성 또는 수정(Alert) 시에 On Delete Cascade 를 써야 한다

- 그럼 부서를 삭제할때 그 부서 소속 직원들 또한 연쇄적으로 삭제된다.



프로젝트 MyBatis2 : 부서 상세 페이지 (해당 부서의 직원들 목록)

- deptList.jsp 에서 부서명을 출력하는 코드에 링크가 걸려있다

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

- 부서명 을 클릭시 "empList.do" 로 요청하고, 이때 그 부서 소속 직원을 구해와야 하므로 부서코드 deptno 를 가져간다

ex) 20 번 부서 부서명 클릭시 20 번 부서에 속하는 직원들을 select SQL문으로 구해와야한다

- 부서코드 deptno 를 통해 Emp 테이블에서 검색을 해야한다

 

Controller 클래스 EmpController.java 에서 "empList.do" 요청 부분만 + @Autowired 부분

	@Autowired
	private EmpService es;
	@Autowired
	private DeptService ds;

	// 부서 상세 페이지
	@RequestMapping("empList.do")
	public String empList(int deptno, Model model) {
		Dept dept = ds.select(deptno);
		List<Emp> list = es.list(deptno);
		model.addAttribute("dept", dept);
		model.addAttribute("list", list);
		return "emp/empList";
	}

- 앞에서 넘어온 네임값과 이름이 같은 변수 deptno 에 앞에서 넘어온 부서코드가 바로 저장됨

- @RequestParam("deptno") 가 int deptno 앞에 생략되어있다

- 상세 페이지에서 2가지 다른 정보를 DB 에서 구해와야하므로, 2개의 Service 객체를 @Autowired 로 구해옴

- Dept 테이블과 연동 위해 DeptService 객체, Emp 테이블과 연동 위해 EmpService 객체 구함

 

상세 페이지에서 2가지 정보를 구해야한다

1. Dept 테이블에서 부서 상세 정보를 구하기

- 아래에 표시한 부분(부서명) 을 뿌려주기 위해 부서 상세 정보도 구해야함

2. Emp 테이블에서 각 부서 소속 직원들 목록 구하기

<돌아온 후>

 

- 돌려 받은 부서 상세 정보를 저장한 Dept 객체 dept, 검색한 사원 정보들을 저장한 List 를 모두 Model 객체에 저장

- 그리고 /WEB-INF/views/emp/empList.jsp 로 이동


1. Dept 테이블에서 부서 상세 정보를 구하기

- select 는 많이 했으므로 코드 생략

 

2. Emp 테이블에서 각 부서 소속 직원들 목록 구하기

 

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

	public List<Emp> list(int deptno) {
		return ed.list(deptno);
	}

 

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

	public List<Emp> list(int deptno) {
		return sst.selectList("empns.list", deptno);
	}

- Emp 테이블에서 특정 부서번호의 직원을 구할 것이므로 여러 데이터를 검색하는 것임, selectList() 를 쓴다

 

Mapper 파일 Emp.xml 에서 id 가 "list" 인 태그 부분만 + resultMap 

	<!-- Use type aliases to avoid typing the full classname every time. -->
	<resultMap id="empResult"    	type="emp">
		<result property="empno" 	column="empno" />
		<result property="ename"  	column="ename" />
		<result property="job"		column="job" />
		<result property="mgr" 		column="mgr" />
		<result property="hiredate" column="hiredate" />
		<result property="sal"	  	column="sal" />
		<result property="comm"	   	column="comm" />
		<result property="deptno"   column="deptno" />
		<result property="dname"	column="dname" />
		<result property="loc"   	column="loc" />
	</resultMap>
	<select id="list" parameterType="int" resultMap="empResult">
		select * from emp where deptno=#{deptno} order by empno
	</select>

- 현재는 꼭 resultMap 을 쓰지 않아도 된다

- Emp 테이블에서 특정 deptno 를 가진 사원들의 데이터를 모두 검색한다

- order by empno 에 의해 사원번호를 기준으로 오름차순 정렬된다

 

View 페이지 empList.jsp

- emp/empList.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>
<script type="text/javascript">
	$(function() {
		$('#list').load('deptList.do');
	});
</script>
</head>
<body>
	<div class="container" align="center">
		<h2 class="text-primary">${dept.dname}직원목록</h2>
		<table class="table table-striped">
			<tr>
				<td>사번</td>
				<td>이름</td>
				<td>업무</td>
				<td>급여</td>
				<td>부서코드</td>
			</tr>
			<tr>
				<c:if test="${empty list }">
					<tr>
						<td colspan="5">직원이 없는 부서입니다</td>
					</tr>
				</c:if>
				<c:if test="${not empty list }">
					<c:forEach var="emp" items="${list }">
						<tr>
							<td>${emp.empno }</td>
							<td><a href="empView.do?empno=${emp.empno}"
								   class="btn btn-info">${emp.ename}</a></td>
							<td>${emp.job}</td>
							<td>${emp.sal}</td>
							<td>${emp.deptno}</td>
						</tr>
					</c:forEach>
				</c:if>
		</table>
		<a href="deptList.do" class="btn btn-success">부서목록</a> 
		<a href="empInsertForm.do" class="btn btn-success">직원 입력</a>
		<div id="list"></div>
	</div>
</body>
</html>

- Model 객체에 저장된 해당 부서의 상세 정보 dept 를 사용해서 ${dept.dname} 으로 상단에 부서명을 출력

- Model 객체에 저장된 해당 부서 소속 사원 목록인 list 를 사용해서 ${list} 를 forEach 의 items 에 써서 사원 데이터를 하나씩 가져온다

- forEach의 emp 변수를 통해 하나씩 가져온 사원 데이터에서 ${emp.empno} 등을 활용해 사원 정보를 출력

- 부서 상세페이지에 출력된 사원 정보에서 사원명을 클릭하면 해당 사원의 상세정보를 보여주기 위해 "empView.do" 로 요청한다 

- 부서 상세페이지에서 "직원 입력" 을 누르면 직원을 새로 등록할 수 있는 직원 입력폼으로 가기 위해 "empInsertForm.do" 로 요청한다

 

아래에 부서 목록 뿌리기 (비동기 처리) (empList.jsp 부분)

- 부서 상세 페이지 하단에도 목록 페이지와 같은 부서 목록을 출력하고 있다

<script type="text/javascript">
	$(function() {
		// deptList.do 요청 결과를 div 태그 사이에 출력
		$('#list').load('deptList.do');
	});
</script>

- id 가 "list"인 div 태그를 구해와서 ajax 의 load() 함수를 사용해서 "deptList.do" 요청 결과 전체를 그 div 태그 사이에 출력

+ 이전에도 비슷한 내용을 했다 : https://laker99.tistory.com/146 

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

 


- 소속 직원이 없는 부서인 50번 부서 상세 페이지

- 참조하는 직원이 없는 부서이므로 삭제 가능하다

 

- 소속 직원이 있는 20번 부서의 상세 페이지

- 사원을 출력할때 사번이 빠른 순서대로 출력된다(오름차순)


프로젝트 MyBatis2 : 사원 등록폼

- 사원 등록 전 사원 등록폼 부터 가야한다

- 부서 상세페이지에서 "직원 입력" 을 누르면 사원을 새로 등록할 수 있는 사원 입력폼으로 가기 위해 "empInsertForm.do" 로 요청한다

		<a href="empInsertForm.do" class="btn btn-success">직원 입력</a>

- "직원 입력" 클릭시 "empInsertForm.do" 로 요청

 

직원 등록폼에서 DB 와 연동해서 할 일

- 직원 입력폼에서 직원을 등록할때 관리자, 부서코드를 가져와야함

- 사원, 부서는 추가할 수 있으므로 고정된 리스트가 아님, 그래서 DB 와 연동해서 가져와야함

- 즉 사원 목록, 부서 목록을 가져와서 이 직원 입력폼에 뿌려야한다

 

Controller 클래스 EmpController.java 에서 "empInsertForm.do" 요청 부분만

	@RequestMapping("empInsertForm.do")
	public String empInsertForm(Model model) {
		List<Dept> deptList = ds.list();
		List<Emp> empList = es.empList();
		model.addAttribute("deptList", deptList);
		model.addAttribute("empList", empList);
		return "emp/empInsertForm";
	}

- 사원을 새로 등록할때 사번이 중복되지 않은지 중복검사를 해야함

DB 에서 가져와야 할 것 2가지

1. 사원 목록

- 관리자를 선택할때 관리자의 사원명은 바뀔 수 있는 값(사원이 추가될 수도 있다) 이므로, 사원명 리스트를 DB 와 연동해서 가져와야함

2. 부서 목록

- 부서명을 선택할때 부서명은 바뀔 수 있는 값(부서가 추가될 수도 있다)이므로, 부서명 리스트를 DB 와 연동해서 가져와야함

- 사원, 부서는 추가될 수 있으므로 DB와 연동해서 그 리스트를 가져와야함

- 즉 부서명, 사원명 을 가져오기 위해 Dept 테이블, Emp 테이블 둘 다 연동해서 부서 목록, 사원 목록 을 가져와야함

 

- 부서 목록 가져오기는 위에서 했으므로 코드 생략

- 사원 목록 가져오기는 위에서 한것과 유사하므로 코드 생략

- 특정 부서의 사원이 아니라 전체 사원을 구한다는 점만 다르다

<돌아온 후>

- 사원 전체 목록, 부서 전체 목록을 모두 Model 객체에 저장 후 empInsertForm.jsp 로 이동

 

- View 페이지인 사원 등록 폼으로 가자

 

View 페이지 empInsert.jsp

- emp/empInsert.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>
<script type="text/javascript">
	$(function() {
		// 사원번호 중복검사
		$('#dupCheck').click(function() {
			var empno = $('#empno').val();
			if (!empno) {
				alert('사번 입력후에 체크');
				$('#empno').focus();
				return false;
			}// $.post("요청이름","전달될 값","콜백함수");
			$.post('dupCheck.do', 'empno=' + empno, function(msg) {
				alert(msg);
			});
		});
	});
</script>
</head>
<body>
	<div class="container">
		<h2 class="text-primary">직원 등록</h2>
		<form action="empInsert.do" method="post">
			<table class="table table-bordered">
				<tr>
					<td>사번</td>
					<td><input type="number" name="empno" required="required"
							   id="empno" autofocus="autofocus"> 
						<input type="button" value="중복" id="dupCheck"></td>
				</tr>
				<tr>
					<td>이름</td>
					<td><input type="text" name="ename" required="required"></td>
				</tr>
				<tr>
					<td>업무</td>
					<td><input type="text" name="job" required="required"></td>
				</tr>
				<tr>
					<td>관리자</td>
					<td><select name="mgr">
							<c:forEach var="emp" items="${empList }">
								<option value="${emp.empno}">${emp.ename}</option>
							</c:forEach>
					    </select>
					</td>
				</tr>
				<tr>
					<td>입사일</td>
					<td><input type="date" name="hiredate" required="required"></td>
				</tr>
				<tr>
					<td>급여</td>
					<td><input type="number" name="sal" required="required"></td>
				</tr>
				<tr>
					<td>보너스</td>
					<td><input type="number" name="comm" required="required"></td>
				</tr>
				<tr>
					<td>부서코드</td>
					<td><select name="deptno">
							<c:forEach var="dept" items="${deptList }">
								<option value="${dept.deptno}">${dept.dname}</option>
							</c:forEach>
						</select>
					</td>
				</tr>
				<tr>
					<td colspan="2"><input type="submit" value="확인"></td>
				</tr>
			</table>
		</form>
	</div>
</body>
</html>

- 가져온 사원, 부서 목록 forEach 태그의 items 태그에 ${empList} , ${deptList} 에 넣는다

- 그 forEach 태그에서 사원 1명 정보를 담은 객체를 가져와서, select 태그 안의 option 에 필요한 필드를 가져와서 뿌리기

- 그 forEach 태그에서 부서 1개 정보를 담은 객체를 가져와서, select 태그 안의 option 에 필요한 필드를 가져와서 뿌리기

<관리자>

- 가져온 관리자 목록에서 관리자를 1명씩 가져와서 그 관리자의 사원명, 사원번호를 꺼낸다

- 관리자를 선택할때, 사원명이 출력되지만 value 는 관리자(상사) 의 사원번호인 empno 이다

- 즉 실제 DB 에 저장되는 값은 관리자의 사원명이 아닌 관리자의 사원번호이다!

<입사일>

- input type 을 "date" 로만 해도 캘린더를 선택할 수 있는 양식이 나타남

<부서>

- 부서를 선택할때, 부서명이 출력되지만 value 는 부서의 부서번호은 deptno 이다

- 즉 실제 DB 에 저장되는 값은 부서의 부서명이 아닌 부서의 부서번호이다!

<"확인" 누른 후>

- 직원 입력폼 empInsertForm.jsp 에서 입력을 한 후 "확인" 을 누르면 "empInsert.do" 로 요청한다

- 이때 이 입력폼에 입력된 값들을 가지고 간다


프로젝트 myBatis2 : 사원 등록폼에서 사번 중복검사 (비동기 처리)

사원 번호 중복 검사 (empInsertForm.jsp 부분)

<script type="text/javascript">
	$(function() {
		// 사원번호 중복검사
		$('#dupCheck').click(function() {
			var empno = $('#empno').val();
			if (!empno) {
				alert('사번 입력후에 체크');
				$('#empno').focus();
				return false;
			}// $.post("요청이름","전달될 값","콜백함수");
			$.post('dupCheck.do', 'empno=' + empno, function(msg) {
				alert(msg);
			});
		});
	});
</script>

- DB 에서 이 사번이 사용가능한지 중복검사를 해야함, 이때 이걸 비동기로 처리하려고 한다

- "중복" 버튼 클릭시 click() 함수 실행됨, 사용자가 입력한 값을 구해오고 그 값이 null 인지 유효성 검사

- 입력 값이 있다면 ajax 기능을 사용해서 post() 함수로 사번 중복검사를 한다

+ 비동기 처리 : 페이지가 바뀌지 않고 처리

 

- 요청 이름값은 "dupCheck.do" 로 한다, 그리고 사용자가 입력한 사번 값을 키 밸류 형식(키=밸류) 으로 전달하며 요청

- 콜백함수로 메세지를 돌려받아서 alert 에 출력

 

ajax post() 함수에 대한 자세한 내용

https://laker99.tistory.com/106 (검색어 : $.post() 함수)

 

Controller 클래스 EmpController.java 에서 "dubCheck.do" 요청 부분만

	// 사원번호 중복검사
	@RequestMapping("dupCheck.do")
	public String dupCheck(int empno, Model model) {
		System.out.println("empno:"+empno);
		Emp emp = es.select(empno);
		String msg = "";
		if (emp != null) // 중복 사원번호
			msg = "이미 있는 데이터입니다";
		else // 사용 가능한 사원번호
			msg = "사용 가능한 사번 입니다";
		model.addAttribute("msg", msg);
		
		// 웹 브라우저에 출력되는 결과가 callback 함수로 리턴된다.
		return "emp/dupCheck";
	}

- 앞에서 키 밸류 형식으로 전달된 사번이, 키값과 같은 이름인 empno 변수로 바로 저장된다

- @RequestParam("empno") 가 int empno 앞에 생략되었다

- 아래에서 empno 를 출력해보면 사용자가 입력한 사번이 출력됨

 

- 그 empno 로 Service 클래스의 select() 를 호출해서 상세 정보를 구해와서 Emp 객체 emp 에 저장

- 만약 이 emp 가 null 이 아니면, 즉 데이터가 존재하면 해당 사번에 해당하는 데이터가 있다는 의미, 즉 중복인 사원번호라는 의미이다

- 사원번호가 중복이면 "이미 있는 데이터입니다", 중복이 아니면 "사용 가능한 사번입니다" 를 msg 변수에 저장

- 브라우저에 출력되는 값이 콜백함수로 리턴되기때문에 출력을 하는 View 페이지로 msg 를 넘겨서 거기서 브라우저 출력을 하자, 그럼 거기서 출력되는 값이 콜백함수로 리턴됨

+ 여기서 out 객체를 생성해서 출력하면 코드가 지저분해짐, msg 출력만을 하기 위한 View 페이지 dubCheck.jsp 를 따로 만든다

 

VIew 페이지 dubCheck.jsp

- dubCheck.jsp

${msg}

- 메세지가 브라우저에 출력되므로 그 결과가 콜백함수로 리턴된다

 

- 다시 empInsertForm.jsp 부분을 보자

<script type="text/javascript">
	$(function() {
		// 사원번호 중복검사
		$('#dupCheck').click(function() {
			var empno = $('#empno').val();
			if (!empno) {
				alert('사번 입력후에 체크');
				$('#empno').focus();
				return false;
			}// $.post("요청이름","전달될 값","콜백함수");
			$.post('dupCheck.do', 'empno=' + empno, function(msg) {
				alert(msg);
			});
		});
	});
</script>

- 콜백함수로 값을 받아서 alert 으로 메세지를 뿌려준다

- 그럼 중복일떄는 "이미 있는 데이터입니다", 중복이 아닐때는 "사용 가능한 사번입니다" 출력


프로젝트 myBatis2 : 사원 등록

- 사원 입력폼 empInsertForm.jsp 에서 입력을 한 후 "확인" 을 누르면 "empInsert.do" 로 요청한다

 

Controller 클래스 EmpController.java 에서 "empInsert.do" 요청 부분만

	// 사원 등록
	@RequestMapping("empInsert.do")
//	public String empInsert(Emp emp, String hiredate1, Model model) {
	public String empInsert(Emp emp, Model model) {
//		emp.setHiredate(Date.valueOf(hiredate1)); // String -> Date 형변환
		int result = es.insert(emp);
		model.addAttribute("result", result);
		model.addAttribute("deptno", emp.getDeptno());
		return "emp/empInsert";
	}

- 이전의 사원 등록폼에서 넘어온 값들을 한번에 @ModelAttribute (생략) 을 통해 DTO Emp 객체 emp 에 세팅

- Service 클래스의 insert() 메소드 호출, 호출하며 사원 등록폼에서 넘어온 값을 저장하는 객체 emp 를 넘겨줌

<돌아온 후>

- insert() 가 호출되어 삽입 성공하면 1 을 반환한다, result 에 그 값을 저장하고 Model 객체에 result 를 저장

- View 인 empInsert.jsp 에서는 삽입 성공시에, 그 사원의 부서 소속 모든 사원을 출력하는 부서 상세페이지로 이동할 것임

- 그러므로 삽입된 사원의 부서번호인 deptno 또한 가져가야한다, Model 객체에 저장 

- /WEB-INF/views/emp/empInsert.jsp 로 이동

 

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

	public int insert(Emp emp) {
		return ed.insert(emp);
	}

 

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

	public int insert(Emp emp) {
		return sst.insert("empns.insert", emp);
	}

 

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

	<insert id="insert" parameterType="emp">
		insert into emp values(#{empno},#{ename},#{job},#{mgr},
			#{hiredate},#{sal},#{comm},#{deptno})
	</insert>

- 객체 emp 를 통해 넘어온 값들을 사용해서 insert 를 해준다

- #{empno} 는 emp.getEmpno() 와 같은 의미

- 입사일(hiredate) 은 캘린더에서 선택한 값이었지만, 객체 emp 에서 입사일은 Date 형으로 저장되어 있다

- Emp DTO 클래스에 Date 형으로 되어있기 때문이다

 

View 페이지 empInsert.jsp

- emp/empInsert.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 = "empList.do?deptno=${deptno}";
		</script>
	</c:if>
	<c:if test="${result <= 0 }">
		<script type="text/javascript">
			alert("입력 실패");
			history.go(-1);
		</script>
	</c:if>
</body>
</html>

- 삽입 성공시 result 가 1 이므로 "입력 성공" 출력 후 그 사원의 부서 상세 페이지로 이동하기 위해 "empList.do" 로 요청

- 요청하면서, 부서 상세 페이지에서는 해당 부서 소속 모든 사원 목록을 보여주므로 deptno 도 GET 으로 전달한다


- 사원 등록은 아직 하지 않았다

- 내일 등록하자

+ Recent posts