[Java] MyBatis

클라우드·2024년 4월 17일
0

Java

목록 보기
19/20
post-thumbnail

1. MyBatis

  • Database 연동을 도와주는 Framework

    💡 기존 바닐라 형태 JDBC는 연동 과정도 복잡하고, 반복적인 코드가 나오면서 SQL 문장이 자바 코드와 섞여 있어서 유지보수가 힘들다. 따라서 이런 점을 개선하여 편의성과 유지보수성을 높이기 위하 목적으로 사용된다.

  • Controller에서 Service를 호출하고 Service에서 DAO를 이용해서 Database를 처리할 때 DAO에서 MyBatis를 사용해서 Database 처리를 하게 된다.

1.1 MyBatis 특징

  • 복잡한 JDBC 코드를 걷어내고 깔끔한 소스 코드를 유지할 수 있다.
  • SQL 실행 결과가 기본적으로 Map에 Mapping 된다.
    • 기본 JDBC는 ResultSet이라는 커서를 리턴 받아서 일반적으로 while을 돌면서 결과 처리를 하게 되는데, 이 과정이 생략되고 자동으로 결과 데이터가 HashMap에 저장된다. HashMap 뿐만 아니라 VO에도 저장이 가능하다.
  • SQL 문장을 자바 소스 코드에서 분리해서 따로 XML 파일로 관리한다.
  • DataSource 기능과 Transaction 처리 기능을 제공한다.
    • DataSource(Connection Pool)
    • Pool은 Resource를 모아놓은 저장소
    • Connection의 집합체 = Connection Pool

2. MyBatis download & 설정

3. XML(SqlMapConfig.xml)파일 생성

  • resources/SqlMapConfig.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>

    <!-- driver properties 파일-->
    <properties resource="./driver.properties" />

    <!-- MyBatis setting에 대한 내용 -->
    <settings>
        <setting name="jdbcTypeForNull" value="NULL"/>
        <!-- 만약 MyBatis의 SQL Query(select)를 이용해서 처리했는데
        데이터베이스에서 가져온 내용이 없다면 Java 객체 NULL로 리턴한다. -->
    </settings>
    <!-- Alias를 설정할 수 있다. 여기서 설정하는 Alias(별칭)은 오직 XML에서만 사용된다.
    단순히 타이핑을 줄이기 위한 용도로 사용된다. -->
    <typeAliases>
        <typeAlias alias="BookVO" type="example.vo.BookVO" />
    </typeAliases>
    
    <!-- Database 연결에 대한 설정
    일반적으로 개발용, 운영용처럼 여러 개의 환경을 설정해놓고
    상황에 맞게 이용하는 방식을 사용한다. -->
    <environments default="development">
        <environment id="development">
            <!-- transactionManager는 type이 2개가 있다.
             JDBC라고 쓰면 수동으로 transaction을 관리한다는 의미
             MANAGED라고 쓰면 Container가 TR을 관리한다는 의미 -->
            <transactionManager type="JDBC">

            </transactionManager>

            <!-- dataSource 설정은 Connection Pool 설정하는 것이다.
            #{}이랑 ${}이랑 의미가 조금 다르다. -->
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>

    <!-- 사용할 Mapper에 대한 설정이 나온다. -->
    <mappers>
        <mapper resource="./sqlmap/Book.xml" />
    </mappers>
</configuration>
  • resources/sqlmap/Book.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="example.mybook">
    <select id="selectByISBNHashMap" parameterType="String" resultType="HashMap">
        <![CDATA[
        SELECT bisbn, btitle, bprice, bauthor
        FROM book
        WHERE bisbn = #{bisbn}
        ]]>
    </select>

    <select id="selectByAllHashMap" parameterType="String" resultType="HashMap">
        <![CDATA[
        SELECT bisbn, btitle, bprice, bauthor
        FROM book
        ]]>
    </select>

    <select id="selectByISBNBookVO" parameterType="String" resultType="BookVO">
        <![CDATA[
        SELECT bisbn, btitle, bprice, bauthor
        FROM book
        WHERE bisbn = #{bisbn}
        ]]>
    </select>

    <resultMap id="result_1" type="BookVO">
        <result property="bisbn" column="my_isbn" jdbcType="VARCHAR" javaType="String" />
        <result property="btitle" column="my_title" />
        <result property="bprice" column="my_price" />
        <result property="bauthor" column="my_author" />
    </resultMap>

    <select id="selectByISBNResultMap" parameterType="String" resultMap="result_1">
        <![CDATA[
        SELECT bisbn AS my_isbn,
               btitle AS my_title,
               bprice AS my_price,
               bauthor AS my_author
        FROM book
        WHERE bisbn = #{bisbn}
        ]]>
    </select>

    <update id="titleUpdate" parameterType="BookVO">
        <![CDATA[
        UPDATE book
        SET btitle = #{btitle}
        WHERE bisbn = #{bisbn}
        ]]>
    </update>
</mapper>
  • resources/driver.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/library?characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
username=root
password=****
  • src/example/dao/BookDAO.java
package example.dao;

import example.vo.BookVO;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;

import java.util.HashMap;
import java.util.List;

public class BookDAO {

    // 여기서 SqlSessionFactory를 직접 얻어내지 않는다.
    // 생성자를 통해 주입받아서 사용한다.
    private SqlSessionFactory sqlSessionFactory;

    public BookDAO() {
    }

    // 생성자 주입 Constructor Injection
    public BookDAO(SqlSessionFactory sqlSessionFactory) {
        this.sqlSessionFactory = sqlSessionFactory;
    }

    // 1. ISBN 번호를 입력으로 받아서 책 1권의 데이터를 HashMap으로 만들어서 리턴하는 method를 작성해보자.
    public HashMap<String, Object> selectByISBNHashMap(String bisbn) {

        HashMap<String, Object> result = null;
        SqlSession session = sqlSessionFactory.openSession();

        try {
            result = session.selectOne("example.mybook.selectByISBNHashMap", bisbn);
        } catch (Exception e) {
            System.out.println(e);
        } finally {
            session.close();
        }
        return result;
    }

    // 2. 모든 책의 데이터를 HashMap의 List로 만들어서 리턴하는 method를 작성해보자.
    public List<HashMap<String, Object>> selectByAllHashMap() {

        List<HashMap<String, Object>> result = null;
        SqlSession session = sqlSessionFactory.openSession();

        try {
            result = session.selectList("example.mybook.selectByAllHashMap");
        } catch (Exception e) {
            System.out.println(e);
        } finally {
            session.close();
        }
        return result;
    }

    // 3. ISBN 번호를 입력으로 받아서 책 1권의 데이터를 BookVO로 만들어서 리턴하는 method를 작성해보자.
    public BookVO selectByISBNBookVO(String bisbn) {

        BookVO result = null;
        SqlSession session = sqlSessionFactory.openSession();

        try {
            result = session.selectOne("example.mybook.selectByISBNBookVO", bisbn);
        } catch (Exception e) {
            System.out.println(e);
        } finally {
            session.close();
        }
        return result;
    }

    // 4. ISBN 번호를 이용해서 책 1권의 정보를 BookVO로 변환해서 가져오기
    // 그러나 table의 column명이 VO의 field명과 다른 경우 하는 방법
    public BookVO selectByISBNResultMap(String bisbn) {

        BookVO result = null;
        SqlSession session = sqlSessionFactory.openSession();

        try {
            result = session.selectOne("example.mybook.selectByISBNBookVO", bisbn);
        } catch (Exception e) {
            System.out.println(e);
        } finally {
            session.close();
        }
        return result;
    }

    // 5. ISBN 번호를 이용해서 책 1권의 정보를 변경하고 싶다.
    // 책 제목을 바꿀 때
    public int titleUpdate(BookVO bookVO) {

        int result = 0;
        SqlSession session = sqlSessionFactory.openSession();

        try {
            result = session.update("example.mybook.titleUpdate", bookVO);
        } catch (Exception e) {
            System.out.println(e);
        } finally {
            session.commit(); // update 계열은 반드시 처리해야 한다. 하지 않으면 데이터베이스에 반영이 안된다.
            session.close();
        }
        return result;
    }
}
  • src/example/mybatis/MyBatisConnectionFactory.java
package example.mybatis;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.Reader;

public class MyBatisConnectionFactory {
    // 실제 프로그램에서 (DAO)에서 사용하는 데이터베이스 연결 객체는
    // 기존에는(JDBC)일 때는 Connection 객체를 이용했다.
    // MyBatis에서는 SqlSession이라는 객체를 이용한다.
    private static SqlSessionFactory sqlSessionFactory;

    // 이 Factory 객체는 해당 클래스로부터 객체가 1개만 만들어져서 사용되도록 처리할 것이다.
    // => Singleton으로 동작하게끔 만든다.
    static {
        try {
            String resource = "./SqlMapConfig.xml";
            Reader reader = Resources.getResourceAsReader(resource);

            if (sqlSessionFactory == null) {
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
            }
        } catch (Exception e) {
            System.out.println(e);
        }
    }

    public static SqlSessionFactory getSqlSessionFactory() {
        return sqlSessionFactory;
    }

}
  • src/example/vo/BookVO.java
package example.vo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class BookVO {
    private String bisbn;
    private String btitle;
    private int bprice;
    private String bauthor;
}
  • src/example/main.java
package example;

import example.dao.BookDAO;
import example.mybatis.MyBatisConnectionFactory;
import example.vo.BookVO;
import org.apache.ibatis.session.SqlSessionFactory;

import java.util.HashMap;
import java.util.List;

public class Main {
    public static void main(String[] args) {

        // 데이터베이스 처리
        // DAO가 있어야 데이터베이스 처리를 할 수 있다.
        // DAO를 만들려면 SqlSessionFactory를 먼저 확보해야 이것을 주입해서 DAO를 만들 수 있다.
        SqlSessionFactory factory = MyBatisConnectionFactory.getSqlSessionFactory();
        BookDAO dao = new BookDAO(factory);

        // 1.
//        HashMap<String, Object> result = dao.selectByISBNHashMap("89-7914-274-9");
//
//        for (String key : result.keySet()) {
//                System.out.println(key + " : " + result.get(key));
//        }

        // 2.
//        List<HashMap<String, Object>> result = dao.selectByAllHashMap();
//        for(HashMap<String, Object> map : result) {
//            for (String key: map.keySet()) {
//                System.out.println(key + " : " + map.get(key));
//            }
//            System.out.println();
//        }

        // 3. ISBN 번호로 책 1권의 정보 BookVO로 변환
//        BookVO result = dao.selectByISBNBookVO("89-7914-274-9");
//        System.out.println(result);

        // 4. ISBN 번호를 이용해서 책 1권의 정보를 BookVO로 변환해서 가져오기
        // 그러나 table의 column명이 VO의 field명과 다른 경우 하는 방법
//        BookVO result = dao.selectByISBNResultMap("89-7914-274-9");
//        System.out.println(result);

        // 5. ISBN 번호를 이용해서 책 1권의 정보를 변경하고 싶다.
        // 책 제목을 바꿀 때
        BookVO bookVO = new BookVO("89-7914-274-9", "나는 고수 프로그래머다.", 0, null);
        int result = dao.titleUpdate(bookVO);
        System.out.println("영향을 받은 행의 개수: " + result);
    }
}
profile
안녕하세요 :)

0개의 댓글