---
title: "Java EE 정리 27"
date: "2019-04-04"
tags: ["Java EE","MyBatis"]
status: archived
verified: false
---

---

## MyBatis 복습

![01](/md/legacy-assets/javaEe/27/01.webp)

* **영쓰 XML 사용 정리**
  * 설정 XML에서 Driver, DBServerURL, ID, Password를 설정하고 이미 만들어준 Mapper들을 연결
        * 설정정보는 Properties로 넣거나 직접 Hard Coding 
  * MyBatis 사용을 위해 설정 XML과 연결된 스트림을 만듦
  * SqlSessionFactoryBuilder 객체를 만들 새로 만들고 설정 XML과 연결된 스트림으로 SqlSessionFactory 객체를 build()로 생성
  * 그리고 만들어진 SqlSessionFactory 객체를 이용, MyBatis Handler인 SqlSession을 openSession()으로 얻는다.
  * SqlSession을 사용하여 insert, update, delete, select 등 작업을 수행

## MyBatis Logging

* **XML안에 쿼리가 있기 때문에 쿼리를 볼 수 없다.**
  * **logging library를 사용하면 Log를 찍어볼 수 있다.**
* MyBatis에서 지원하는 [Log4j Logging](http://www.mybatis.org/mybatis-3/logging.html)을 사용
* classpath에 `log4j.properties`란 파일을 생성
  
```
# Global logging configuration
log4j.rootLogger=ERROR, stdout
# MyBatis logging configuration...
# log4j.logger.org.mybatis.example.BlogMapper=TRACE 추척할 녀석인데 안써도 됨
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
```

![02](/md/legacy-assets/javaEe/27/02.webp)


* **Log4J 설치**
  * [Log4J 사이트](https://logging.apache.org/log4j/2.x/)
  
![03](/md/legacy-assets/javaEe/27/03.webp)

![04](/md/legacy-assets/javaEe/27/04.webp)


* **LOG 레벨**
  * **로그를 출력하게 되는 시점**
  * **TRACE > DEBUG > INFO > WARNING > ERROR > FATAL**
    * **TRACE** - 쿼리가 실행만 되면 출력
    * **DEBUG** - Exception 떨어졌을 때 쿼리를 출력
    * **FATAL** - 심각한 오류가 있을 때 출력
* **로그 사용 시 장점은 프로그램과 에러 출력 코드를 분리할 수 있다.**
  * **코드가 간결해진다.**
  * **출력 시점을 마음대로 변경할 수 있다.**

```
# Global logging configuration
log4j.rootLogger=TRACE, stdout
# 로그 사용 시 장점 : 프로그램과 에러 출력 코드를 분리할 수 있다
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
```

* **사용할땐 useLog4JLogging() 호출**

```java
public class UseMyBatis {
      public UseMyBatis() { // 생성자에 로깅객체를 생성
            org.apache.ibatis.logging.LogFactory.useLog4JLogging();
      }
...
```

![05](/md/legacy-assets/javaEe/27/05.webp)

* **로그를 파일로 저장하는 Logging 설정파일**

```
# Global logging configuration
log4j.rootLogger=TRACE, file,stdout
# MyBatis logging configuration...
#log4j.logger.org.mybatis.example.BlogMapper=TRACE
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

log4j.appender.file=org.apache.log4j.DailyRollingFileAppender
log4j.appender.file.File=C:/dev/mybatis.log
log4j.appender.Threshold=DEBUG
log4j.appender.file.Append=true
log4j.appender.file.DatePattern='.'yyyy-MM-dd'.log'
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p], %d{yyyy-MM-dd HH:mm:ss}, %m %n
log4j.appender.file.encoding=UTF-8
```

![06](/md/legacy-assets/javaEe/27/06.webp)


## MyBatis Properties

* **XML에 접속 정보를 집어넣어 사용하면 값을 value에 넣게 됨(Hard Coding)**
  * 배포되고 운용할 때 "를 지우는 등 잘못 변경해서 문제가 발생하면 개발자가 직접 찾아가서 고쳐줘야한다..
  * **Properties를 사용하면 사용자가 코드를 변경하지 않고 properties파일만 변경하면 됨**

![07](/md/legacy-assets/javaEe/27/07.webp)

* **접속정보를 저장할 database_properties 파일 생성 후 UTF-8로 변경**

![08](/md/legacy-assets/javaEe/27/08.webp)


* **properties는 "이름=값"의 형태만 가진 설정파일**
  * **설정 정보를 가장 간단하게 저장할 수 있는 형식**
  * 공백을 설정하면 값을 가져가는 곳에도 공백이 포함되므로 **절대 공백을 사용하지 않는다**
* **data_properties**
  * **사용자는 설정파일(mybatis_config.xml)을 안건드리고 properties 파일만 변경하면 된다.**

```
driver=oracle.jdbc.OracleDriver
url=jdbc:oracle:thin:@127.0.0.1:1521:orcl
userid=scott
userpass=tiger
```

![09](/md/legacy-assets/javaEe/27/09.webp)


* **mybatis_config.xml**
  * **DTD를 확인하면 properties는 가장 먼저 오고 ?(0 또는 1개 존재)**

```html
...
<configuration>
  <properties  resource="properties.database_properties"></properties>
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${userid}"/>
        <property name="password" value="${userpass}"/>
      </dataSource>
    </environment>
  </environments>
...
```

## MyBatis를 사용한 다양한 SELECT 예(1)

* **MyBatis SELECT 예제들**
  * 컬럼 하나에 레코드 하나 조회할 때
  * 컬럼 여러개 레코드 하나 조회할 때
  * 컬럼 하나에 레코드 여러개 조회할 때
  * 컬럼 여러개 레코드 여러개 조회할 때
  * \> 의 조회
  * \&lt; 의 조회
  * 동일 쿼리의 처리
  * LIKE 의 조회
  * subquery 의 조회
  * union 의 조회 
  * join 의 조회
  * join + subquery 의 조회
  * 컬럼명 또는 테이블명이 동적일 때 조회
  * dynamic query : if, choose, foreach

* 예제를 위한 패키지 생성 후 DAO, mybatis_config.xml을 생성

![10](/md/legacy-assets/javaEe/27/10.webp)

* **mybatis_config.xml**

```html
<?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="properties.database_properties"></properties>
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${userid}"/>
        <property name="password" value="${userpass}"/>
      </dataSource>
    </environment>
  </environments>
  <mappers>
    <mapper resource="kr/co/sist/exam/mapper/exam_mapper1.xml"/>
    <mapper resource="kr/co/sist/exam/mapper/exam_mapper2.xml"/>
  </mappers>
</configuration>
```

* **DAO는 싱클턴으로 객체를 만들고 SqlSessionFactory를 반환하는 메소드를 만들어 둔다.**

```java
public class MyBatisDAO {
      
      private static MyBatisDAO mb_dao; // 싱글턴
      private SqlSessionFactory ssf;
      
      public static MyBatisDAO getInstance() {
            if(mb_dao == null) {
                  org.apache.ibatis.logging.LogFactory.useLog4JLogging(); // 로깅  사용
                  mb_dao = new MyBatisDAO();
            }
            return mb_dao;
      }
      
      public synchronized SqlSessionFactory getSessionFactory() { 
            //  SqlSessionFactory는 Thread에 한개만 생성되서 사용, synchronized로 동시접근을 막는다.
            Reader r = null;
            try {
                  // 1. 설정용 XML파일 연결 스트림 생성
                  r =  Resources.getResourceAsReader("kr/co/sist/exam/dao/mybatis_config.xml");
                  // 2. SqlSessionFactoryBuilder 생성
                  SqlSessionFactoryBuilder ssfb = new  SqlSessionFactoryBuilder();
                  // 3. 연결스트림을 이용, DB와 연동 된  SqlSessionFactory 객체를 받는다.
                  ssf = ssfb.build(r);
                  if (r != null) { r.close(); } // SqlSessionFactory와  연결 시킨 스트림은 끊는다.
            
            } catch (IOException e) {
                  e.printStackTrace();
            }
            
            return ssf;
      }
}
```

* **MyBatis를 이용하면 DBCP와는 다르게 Tomcat없이 단위테스트를 할 수 있다.**

```java
// MyBatisDAO
...
      public static void main(String[] args) { // 단위테스트용
            System.out.println(MyBatisDAO.getInstance().getSessionFactory());
      }
}
```

![11](/md/legacy-assets/javaEe/27/11.webp)

* 기존 예제 메뉴바를 뜯어고치고..

![12](/md/legacy-assets/javaEe/27/12.webp)

* **SELECT 수행 결과를 보여줄 main.jsp를 만듦**
  * **page 파람값에 따라 다른 페이지를 import**

```html
<!-- main.jsp -->
...
<c:if test="${ not empty param.page }">
      <c:import url="/date0404/${ param.page }.jsp"/>
</c:if>
...
```

## 컬럼 하나에 레코드 하나 조회할 때

* **MyBatis에서는 Java의 데이터형(기본형, 참조형)을 그대로 사용할 수 있다.**

```html
<!-- exam_mapper1.xml -->
<mapper namespace="kr.co.sist.exam1">
      <!-- 컬럼 하나에 레코드 하나 조회할 때 -->
      <select id="singleColumn" resultType="String">
            SELECT dname
            FROM dept
            WHERE deptno=#{deptno}
      </select>
...
```

```java
// MyBatisDAO
...
      public String selectSingleColumn(int deptno) {
            String dname = "";
            
            // MyBatis Handler를 사용하여 Mapper(xml)에 있는 ID를 찾고  Parsing 하여
            // 조회된 결과를 얻는다.
            MyBatisDAO.mb_dao = MyBatisDAO.getInstance();
            SqlSession ss = mb_dao.getSessionFactory().openSession();
            
            dname = ss.selectOne("singleColumn", deptno);
            ss.close();
            
            return dname;
      }
...
```

```java
public class MyBatisService { // Controller에서 Serivce 메서드들을 사용
      public String deptName(int deptno) {
            MyBatisDAO mb_dao = MyBatisDAO.getInstance();
            String dname ="";
            dname = mb_dao.selectSingleColumn(deptno)+"부서";
            return dname;
      }
}
```

```html
<!-- single_column.jsp -->
<%@page import="kr.co.sist.exam.service.MyBatisService"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
      MyBatisService mbs = new MyBatisService();

      int deptno = 10;
      String dname = mbs.deptName(deptno);
%>
<%=deptno%>번 부서는 <strong><%=dname %></strong>입니다.
```

![13](/md/legacy-assets/javaEe/27/13.webp)

## 컬럼 하나에 레코드 여러개 조회할 때

```html
<!-- exam_mapper1.xml -->
...
      <select id="multiRow" resultType="integer">
            SELECT deptno
            FROM dept
      </select>
...
```

```java
//MyBatisDAO
...
      public List<Integer> multiRow() {
            List<Integer> list = null;
            
            SqlSession ss = getInstance().getSessionFactory().openSession();
            list = ss.selectList("multiRow");
            ss.close();
            
            return list;
      }
      
...
```

```java
//MyBatisService
...
      public List<Integer> multiRow() {
            List<Integer> list = null;
            
            MyBatisDAO mb_dao = MyBatisDAO.getInstance();
            list = mb_dao.multiRow();
            
            return list;
      }
...
```

```html
<!-- main_menu.jsp -->
...
<li><a  href="main.jsp?page=single_column">컬럼 하나에 레코드 여러개 조회</a></li>
...
```

```html
<!-- multi_row.jsp -->
<%@page import="java.util.List"%>
<%@page import="kr.co.sist.exam.service.MyBatisService"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%
      MyBatisService mbs = new MyBatisService();
      List<Integer> deptnoList = mbs.multiRow();
      pageContext.setAttribute("deptnoList", deptnoList);
%>
<label>부서번호</label>
<select name="deptno">
      <c:forEach var="deptno" items="${ deptnoList }">
      <option value="${ deptno }"><c:out value="${ deptno }"  escapeXml="false"/></option>  
      </c:forEach>
</select>
```

![14](/md/legacy-assets/javaEe/27/14.webp)

## 컬럼 여러개에 레코드 하나 조회할 때

* **typeAlias 노드 : Domain이나 VO를 미리 등록해두고 짧은 이름으로 사용할 때 사용**
  * **여러번 사용될 때 좋음, 한번만 사용되면 안쓰는게 좋다**
  * iBATIS에선 mapper에 정의
  * **MyBatis에선 설정파일(mybatis_config.xml)에 정의**

```html
<!-- mybatis_config.xml -->
...
<configuration>
      <properties  resource="properties/database_properties"></properties>
      <typeAliases>
            <typeAlias type="kr.co.sist.exam.domain.DeptInfo" alias="di" />
      </typeAliases> 
...
```

* **Framework 사용 시 생성자 안만들고 getter/setter만 만든다**

```java
public class DeptInfo {
      private String dname, loc;
      // 생성자 안만들고 getter랑 setter만 만듦
      ...
```

* **조회되는 컬럼은 대소문자를 구분하지 않지만 setter method는 대소문자를 구분**
  * iBATIS에서는 컬럼명 또는 컬럼명 as setter명
* **resultType값은 패키지명.Domain명 또는 typeAlias의 id가 사용됨**

```html
<mapper namespace="kr.co.sist.exam1">
...
      <!-- 컬럼 하나에 레코드 여러개 조회할 때 -->
      <select id="multiColumn" resultType="di">
            SELECT dname, loc
            FROM dept
            WHERE deptno=10
      </select>
...
```

```java
// MyBatisDAO
...
      public DeptInfo multiColumn() {
            DeptInfo di = null;
            
            MyBatisDAO.mb_dao = MyBatisDAO.getInstance();
            SqlSession ss = mb_dao.getSessionFactory().openSession();
            
            di = ss.selectOne("multiColumn");
            ss.close();
            
            return di;
      }
...
```

```java
// MyBatisService
...
      public DeptInfo deptInfo() {
            MyBatisDAO mb_dao = MyBatisDAO.getInstance();
            DeptInfo di = mb_dao.selectMultiColumn();
            
            return di;
      }
}
```

```html
<!-- main_menu.jsp -->
...
<li><a  href="http://localhost:8080/mybatis_prj/date0404/main.jsp?page=multi_column">컬럼 여러개에 레코드 하나 조회</a></li>
...
```

```html
<!-- multi_column.jsp -->
<%@page import="kr.co.sist.exam.domain.DeptInfo"%>
<%@page import="kr.co.sist.exam.service.MyBatisService"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
      MyBatisService mbs = new MyBatisService();
      DeptInfo di = mbs.deptInfo();
%>
10번 부서정보<br/>
부서명 : <strong><%= di.getDname() %></strong>
위치 : <strong><%= di.getLoc() %></strong>
```

![15](/md/legacy-assets/javaEe/27/15.webp)
