> For the complete documentation index, see [llms.txt](https://docs.sysout.co.kr/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.sysout.co.kr/web/back-end/what-is-spring-framework/spring-jdbc/mybatis.md).

# myBatis

## myBatis란

myBatis란 개발자가 지정한 SQL, 프로시저, 고급 매핑을 지원하는 [퍼시스턴트 프레임워크](https://ko.wikipedia.org/wiki/%ED%8D%BC%EC%8B%9C%EC%8A%A4%ED%84%B4%EC%8A%A4_%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC)이다.\
JDBC의 상당부분을 자동화해주어 개발을 빠르게 할 수 있도록 도와주며 데이터베이스 관련 설정들도 쉽게 구현할 수 있도록 해준다.\
상당히 많은 비율의 회사들이 개발에 myBatis를 사용하고 있으며, 이를 대체할 수 있는 수단으로는 [JPA](https://spring.io/projects/spring-data-jpa)가 있다.

## 의존성 설정

Spring에서 myBatis를 사용하기 위해 필요한 의존성은 다음과 같다.

* MyBatis
* MyBatis Spring

pom.xml에 필요한 의존성 정보를 추가한다.

### pom.xml

```xml
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.2</version>
</dependency>

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>2.0.2</version>
</dependency>
```

버전은 상황에 따라 최신/안정화 버전을 설정한다.

## Spring Bean 등록

`JdbcTemplate`을 사용할 때와 마찬가지로 `myBatis`를 이용하기 위해서는 Spring Bean을 등록해야 한다.\
다음 두 가지의 bean을 `root-context.xml`에 등록한다.(뷰와 연관이 없기 때문)

* `org.mybatis.spring.SqlSessionFactoryBean` myBatis 중앙 제어 도구
* `org.mybatis.spring.SqlSessionTemplate` myBatis 명령 실행 도구

### root-context.xml

```xml
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="연결할 dataSource의 ID"></property>
    <property name="configLocation" value="마이바티스 설정파일 위치"></property>
    <property name="mapperLocations" value="마이바티스 명령(mapper)파일 위치를 패턴으로 기재"></property>
 </bean>

 <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
    <constructor-arg index="0" ref="sqlSession"></constructor-arg>
 </bean>
```

## myBatis 기본 설정

`myBatis` 기본 설정파일은 다음과 같은 DTD 선언과 기본 영역이 필요하다.

```xml
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

</configuration>
```

선언되지 않은 경우에는 오류가 발생한다.

우리는 `myBatis`를 `Spring`에 붙여서 사용하기 때문에 `Spring` 설정이 더 중요하다. 따라서 계정 등의 관리 정보는 모두 Spring 설정으로 구현되어 있고, myBatis에서는 크게 할 설정이 없다.\
몇 가지 필요한 설정에 대해서 살펴보면 다음과 같다.

* `<settings>` 관리 설정
* `<typeAliases>` 별칭 설정

특히 별칭 설정이 중요한데, 별칭 설정은 전체 경로를 줄여주는 효과를 가진다.\
예를 들어 `com.hakademy.test.entity.TestDto`라는 클래스가 있을 경우 별칭을 부여하여 `testDto`와 같이 줄일 수 있다.

샘플 코드는 다음과 같다.

```xml
<typeAliases>
    <typeAlias type="com.hakademy.test.entity.TestDto" alias="testDto"/>
</typeAliases>
```

오타가 발생할 경우 해당 파일을 찾지 못하여 실행하자마자 오류가 발생하므로 주의하도록 한다.

## mapper 기본 설정

mapper는 실행 가능한 데이터베이스 명령을 식별 가능한 형태로 보관해두는 파일이다.\
mapper의 기본 DTD 선언과 영역 설정은 다음과 같다.

```xml
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="영역 별칭">

</mapper>
```

`namespace`는 외부에서 접근하기 위한 별칭을 작성하는 곳이며, 다른 namespace와 중복될 경우 오류가 발생한다.

`<mapper>` 내부에는 다음 설정들을 작성한다.

* `<insert>` Create 코드 작성 영역
* `<select>` Read 코드 작성 영역
* `<update>` Update 코드 작성 영역
* `<delete>` Delete 코드 작성 영역

### 테이블 예시

다음과 같은 테이블과 DTO가 있다고 가정하고 CRUD를 진행한다.

> table student

| no | name | score | reg |
| -- | ---- | ----- | --- |
|    |      |       |     |
|    |      |       |     |

> com.hakademy.test.entity.StudentDto.java

```java
package com.hakademy.test.entity;

@Data @NoArgsConstructor @AllArgsConstructor @Builder
public class StudentDto{
    private int no;
    private String name;
    private int score;
    private String reg;
}
```

편의를 위해 `myBatis 설정파일`에 다음과 같이 별칭이 등록되어있다고 가정한다.

```xml
<typeAliases>
    <typeAlias type="com.hakademy.test.entity.StudentDto" alias="student"/>
</typeAliases>
```

### insert 구문 예시

데이터를 추가하기 위한 insert 구문은 다음과 같다.

```xml
<insert id="regist" parameterType="student">
    insert into student values(student_seq.nextval, #{name}, #{score}, sysdate)
</insert>
```

* `id` : 구문을 호출하기 위한 식별자이며 namespace 내에서 유일해야 한다.
* `parameterType` : 이 명령을 실행하기 위하여 필요한 데이터의 형태를 1개만 작성할 수 있다.
* `#{name}` : student라는 parameterType에 대하여 작업을 수행하므로 `student.getName()`와 같이 해석된다.
* `#{score}` : student라는 parameterType에 대하여 작업을 수행하므로 `student.getScore()`와 같이 해석된다.

태그 형식이므로 엔터나 띄어쓰기 등이 좀 더 자유롭다.

### select 구문 예시

목록을 구하기 위한 select 구문은 다음과 같다.

```xml
<select id="list" resultType="student">
    select * from student order by no asc
</select>
```

* `id` : 구문 식별자
* `resultType` : 이 명령을 실행했을 때 기대되는 결과물의 형태로 student라고 적을 경우 단일개체, 다중개체 모두 해당된다.

이름으로 검색하는 구문의 예시는 다음과 같다.

```xml
<select id="findByName" resultType="student" parameterType="String">
    select * from student where name = #{name} order by no asc
</select>
```

* `id` : 구문 식별자
* `resultType` : 결과값의 형태로 이 구문은 student(com.hakademy.test.entity.StudentDto)가 나옴
* `parameterType` : 실행에 필요한 데이터는 이름값이므로 string(String)이라고 표기(내장된 별칭에 의해 대소문자 구분하지 않음)

만약 항목과 검색어를 받아서 검색하는 경우에는 다음과 같이 작성할 수 있다.

```xml
<select id="find" resultType="student" parameterType="map">
    select * from student where ${type} like '%'||#{keyword}||'%' order by no asc
</select>
```

* resultType은 여전히 `student`이다.
* parameterType은 `map(java.util.Map)`이며, 이 때 데이터를 key=value 형태로 첨부하면 이름으로 사용이 가능하다.
* ${type}은 정적 바인딩으로 값이 그 자리에 그대로 출력되며 parameterType이 `map`이기 때문에 `map.get("type")`으로 해석된다.
* \#{keyword}는 동적 바인딩으로 값이 형태에 따라 출력되며 parameterType이 `map`이기 때문에 `map.get("keyword")`로 해석된다.

만약 목록과 검색을 한 번에 수행하는 구문을 만들고 싶다면 다음과 같이 조건을 사용할 수 있다.

```xml
<select id="searchAndList" resultType="student" parameterType="map">
    select * from student 
    <if test="type != null and keyword != null">
        where ${type} like '%'||#{keyword}||'%'
    </if>
    order by no asc
</select>
```

`myBatis`의 조건 구문은 JSTL과 매우 유사한 형태를 보이며, &와 같은 기호들도 xhtml에서 사용하기 힘들기 때문에 단어형 연산도 지원한다.

만약 구문에 부등호처럼 xhtml에서 사용 불가한 기호를 써야 할 경우에는 다음과 같이 작성한다.

```xml
<select id="findGreatStudent" resultType="student" parameterType="int">
<![CDATA[
    select * from student where score > #{score}
]]>
</select>
```

`<![CDATA[ ]]>` 표시가 있을 경우 사이에 있는 글자들은 모두 일반 글자로 간주되므로 필요할 경우 사용해야 한다.

## SqlSession을 이용한 CRUD 수행

`myBatis`에 만들어 둔 구문을 사용하고 싶다면 `sqlSession`을 불러와야 한다.\
`SqlSessionTemplate`의 상위 형태인 `SqlSession`으로 저장하여 사용하는것이 일반적이다.

Spring에서는 다음과 같이 사용한다.

```java
@Autowired
private SqlSession sqlSession;
```

CRUD 명령을 수행하려면 구문의 namespace와 id를 알아야 한다.

```xml
<mapper namespace="test">
    <insert id="create" ...>
        insert into...
    </insert>
</mapper>
```

위의 구문을 부르기 위한 명령은 다음과 같다.

```java
sqlSession.insert("test.create", ...);
```

sqlSession은 CRUD에 맞게 각각의 명령을 가지고 있으며, 주요 명령은 다음과 같다.

* insert(C)
  * `sqlSession.insert("구문ID", 첨부데이터)`
* select(R)
  * `sqlSession.selectOne("구문ID", 첨부데이터)` : 단일조회
  * `sqlSession.selectList("구문ID", 첨부데이터)` : 목록조회
* update(U)
  * `sqlSession.update("구문ID", 첨부데이터)`
* delete(D)
  * `sqlSession.delete("구문ID", 첨부데이터)`

첨부데이터는 1개만 가능하며, 여러개일 경우 `객체` 또는 `Map<String, Object>` 형태로 이름을 부여하여 첨부해야 한다.

## DAO를 이용한 CRUD 수행

DAO를 이용하여 CRUD를 수행할 경우에는 DAO에 `SqlSession` 을 Autowired 하여 사용한다.
