• Home
  • About
    • Young's Github Pages photo

      一日不作一日不食

    • About
    • Github
  • Posts
    • All Posts
    • All Tags
  • Projects

Java EE 정리 27

04 Apr 2019

Reading time ~6 minutes

Java EE 정리 27 - MyBatis(2)


MyBatis 복습

01

  • 영쓰 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을 사용
  • 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

  • Log4J 설치
    • Log4J 사이트

03

04

  • 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() 호출
public class UseMyBatis {
      public UseMyBatis() { // 생성자에 로깅객체를 생성
            org.apache.ibatis.logging.LogFactory.useLog4JLogging();
      }
...

05

  • 로그를 파일로 저장하는 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

MyBatis Properties

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

07

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

08

  • 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

  • mybatis_config.xml
    • DTD를 확인하면 properties는 가장 먼저 오고 ?(0 또는 1개 존재)
...
<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 예제들
    • 컬럼 하나에 레코드 하나 조회할 때
    • 컬럼 여러개 레코드 하나 조회할 때
    • 컬럼 하나에 레코드 여러개 조회할 때
    • 컬럼 여러개 레코드 여러개 조회할 때
    • > 의 조회
    • < 의 조회
    • 동일 쿼리의 처리
    • LIKE 의 조회
    • subquery 의 조회
    • union 의 조회
    • join 의 조회
    • join + subquery 의 조회
    • 컬럼명 또는 테이블명이 동적일 때 조회
    • dynamic query : if, choose, foreach
  • 예제를 위한 패키지 생성 후 DAO, mybatis_config.xml을 생성

10

  • mybatis_config.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="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를 반환하는 메소드를 만들어 둔다.
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없이 단위테스트를 할 수 있다.
// MyBatisDAO
...
      public static void main(String[] args) { // 단위테스트용
            System.out.println(MyBatisDAO.getInstance().getSessionFactory());
      }
}

11

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

12

  • SELECT 수행 결과를 보여줄 main.jsp를 만듦
    • page 파람값에 따라 다른 페이지를 import
<!-- main.jsp -->
...
<c:if test="${ not empty param.page }">
      <c:import url="/date0404/${ param.page }.jsp"/>
</c:if>
...

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

  • MyBatis에서는 Java의 데이터형(기본형, 참조형)을 그대로 사용할 수 있다.
<!-- exam_mapper1.xml -->
<mapper namespace="kr.co.sist.exam1">
      <!-- 컬럼 하나에 레코드 하나 조회할 때 -->
      <select id="singleColumn" resultType="String">
            SELECT dname
            FROM dept
            WHERE deptno=#{deptno}
      </select>
...
// 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;
      }
...
public class MyBatisService { // Controller에서 Serivce 메서드들을 사용
      public String deptName(int deptno) {
            MyBatisDAO mb_dao = MyBatisDAO.getInstance();
            String dname ="";
            dname = mb_dao.selectSingleColumn(deptno)+"부서";
            return dname;
      }
}
<!-- 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

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

<!-- exam_mapper1.xml -->
...
      <select id="multiRow" resultType="integer">
            SELECT deptno
            FROM dept
      </select>
...
//MyBatisDAO
...
      public List<Integer> multiRow() {
            List<Integer> list = null;
            
            SqlSession ss = getInstance().getSessionFactory().openSession();
            list = ss.selectList("multiRow");
            ss.close();
            
            return list;
      }
      
...
//MyBatisService
...
      public List<Integer> multiRow() {
            List<Integer> list = null;
            
            MyBatisDAO mb_dao = MyBatisDAO.getInstance();
            list = mb_dao.multiRow();
            
            return list;
      }
...
<!-- main_menu.jsp -->
...
<li><a  href="main.jsp?page=single_column">컬럼 하나에 레코드 여러개 조회</a></li>
...
<!-- 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

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

  • typeAlias 노드 : Domain이나 VO를 미리 등록해두고 짧은 이름으로 사용할 때 사용
    • 여러번 사용될 때 좋음, 한번만 사용되면 안쓰는게 좋다
    • iBATIS에선 mapper에 정의
    • MyBatis에선 설정파일(mybatis_config.xml)에 정의
<!-- 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만 만든다
public class DeptInfo {
      private String dname, loc;
      // 생성자 안만들고 getter랑 setter만 만듦
      ...
  • 조회되는 컬럼은 대소문자를 구분하지 않지만 setter method는 대소문자를 구분
    • iBATIS에서는 컬럼명 또는 컬럼명 as setter명
  • resultType값은 패키지명.Domain명 또는 typeAlias의 id가 사용됨
<mapper namespace="kr.co.sist.exam1">
...
      <!-- 컬럼 하나에 레코드 여러개 조회할 때 -->
      <select id="multiColumn" resultType="di">
            SELECT dname, loc
            FROM dept
            WHERE deptno=10
      </select>
...
// 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;
      }
...
// MyBatisService
...
      public DeptInfo deptInfo() {
            MyBatisDAO mb_dao = MyBatisDAO.getInstance();
            DeptInfo di = mb_dao.selectMultiColumn();
            
            return di;
      }
}
<!-- main_menu.jsp -->
...
<li><a  href="http://localhost:8080/mybatis_prj/date0404/main.jsp?page=multi_column">컬럼 여러개에 레코드 하나 조회</a></li>
...
<!-- 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



Java EEMyBatis