공부/Spring

ORM - Mybatis

Egomi 2017. 4. 10. 12:50

ORM은 Object-relational mapping 이다. 뭐라고? 그냥 DB 쓰기 편하게 해주는 프레임워크다.

기존에 DB를 가져다 쓸 때 일일이 JDBC를 생성해줬던 번거로움을 줄여주는 아주 편한 Framework이다.





기존에 일일이 JDBC로 Data를 가져왔던 것을, 이제는 그냥 자동으로 매핑해주는 Framework을 이용해 사용하기만 하면 된다.


ORM에는 대표적으로 3가지가 있다.

1. Mybatis(한국 多 사용) 

(엄밀히 따지면 ORM은 아닌 듯. ORM은 쿼리문 없이 자동으로 객체 속성과 필드명 매치해줌)

2. Hibernate (전 세계적 多 사용)

3. JPA


google.trends에 쳐보면 Mybatis는 한국에서만 많이 사용하는데, 정부 표준 프레임워크 때문이란다.

그래서 나도 mybatis를 배운다.


< 구글 트랜즈 데이터 비교 >





Mybatis를 사용하기 위해선, 다음과 같이 행해야 한다.


라이브러리 다운 -> 지시서 작성 -> 디스패처에게 지시서 전달 -> 컨트롤러단에서 매핑된 데이터 이용


차근차근 살펴보자!


우선 라이브러리를 다운받는다.


 mybatis와 spring에서 mybatis를 사용할 수 있는 mybatis spring을 다운받아야한다. (DI해줘라.)



그리고 지시서를 만들어야한다.

지시서는  DB와 entity를 매핑시켜준다는 의미로 mapper라고 칭한다. 

흔히 우리는 Spring에서 디스패처에게 지시서를 전달해줄때 두 가지 방식을 이용했다.

1. 지시서(xml파일)

2. 어노테이션매핑



그럼 먼저, 지시서를 이용해보자.


1. xml 파일로 매핑해주기


@noticeDaoMapper.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
<?xml version="1.0" encoding="UTF-8"?>
 
<!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 
<mapper namespace="com.newlecture.web.data.dao.NoticeDao">
     <select id="getList" resultType="com.newlecture.web.data.view.NoticeView">
        SELECT * 
        FROM NOTICE_VIEW 
        WHERE BINARY ${param2} LIKE '%${param3}%'  
        LIMIT 0,10
    </select>
    <!-- $는 ''가 오지 않고, #은 자동으로 ''가 들어간다. -->
    
    <select id="getSize">
        SELECT COUNT(CODE) SIZE 
        FROM NOTICE 
        WHERE BINARY ${param1} LIKE '%${param2}%'
    </select>
    
     <select id="get" resultType="com.newlecture.web.data.view.NoticeView">
        SELECT * 
        FROM NOTICE_VIEW 
        WHERE CODE= #{code}
    </select>
 
    <select id="getPrev">
        SELECT * 
        FROM NOTICE_VIEW 
        WHERE CAST(CODE AS unsigned) &gt; CAST(#{code} AS unsigned) 
        ORDER BY REGDATE asc 
        LIMIT ${()param1-1)*10},10
    </select>
    
    <select id="getNext">
        SELECT * 
        FROM NOTICE_VIEW 
        WHERE CAST(CODE AS unsigned) &lt; CAST(#{code} AS unsigned) 
        ORDER BY REGDATE DESC LIMIT 0,1    
    </select>
    
    <select id="lastCode" resultType="String">
        SELECT MAX(CAST(CODE AS UNSIGNED)) CODE 
        FROM NOTICE
    </select>
    
    <delete id="delete">
        DELETE FROM NOTICE WHERE CODE=#{code}
    </delete>
    
    <!-- 객체를 전달받았을 때 사용법 -> el표기법과 비슷하다. -->
    <!-- getter 가져오는 el표기법과 유사함. -->
    
    <update id="update">
        UPDATE NOTICE 
        SET TITLE=#{title}, CONTENT=#{content} 
        WHERE CODE=#{code}
    </update>
    
    <insert id="add">
        실행 전에 String code에 sql결과를 담는다.
        <selectKey order="BEFORE" keyProperty="code" resultType="String">
            SELECT MAX(CAST(CODE AS UNSIGNED))+1 CODE 
            FROM NOTICE
        </selectKey>
        INSERT INTO NOTICE(CODE, TITLE, WRITER, CONTENT) 
        VALUES (#{code},#{title},#{writer},#{content})
    </insert>
 
    
    
    
</mapper>
cs



mapper의 namespace에 Dao를 명시한다. 여기서 받아온 인자를 이용해 매핑할 수 있다.


단일 인자의 경우, 이름으로 접근 가능하다.

그러나 다중 인자의 경우, param 으로 접근해야한다. ex)param1, param2, param3 등


인자를 이용하는 방식은 2가지 방식이 있다.

${} 나 #{} 태그를 이용하는 것이다.


${}는 가져온 파라미터의 변수형에 관계 없이 가져온다

#{}은 값을 가져온다


ex) String n = "하하";


${n} => 하하

#{n} => '하하'

'%${n}%'=> '%하하%'

'%#{n}%' => '%'하하'%'


이중 쿼리의 경우, <selectKey> 태그를 넣어 메인 sql실행 전에 서브 쿼리가 실행되도록 한다.


<SelectKey> 옵션

order="before" 옵션 - 메인 쿼리 실행 전, 서브쿼리 실행

keyProperty = "code" 옵션 - 쿼리 결과를 가져올 이름

resultType = "String" 옵션 - 쿼리 결과의 반환 타입



2. Annotation(@)로 매핑해주기

엔티티를 명시한 java 인터페이스 파일 위에 sql문을 매핑해주는 annotation이용 시 xml 작성 없이 DB서 데이터를 가져올 수 있다.


sql문을 한 줄에 작성하지 않아도 되는 xml과 달리, 한 줄에 명시해줘야하므로 sql문이 길어지는 경우 불편함을 가져올 수 있다. 그럼에도 불구하고, 많은 사용자들이 이 방식을 이용한다. 


@NoticeDao.java(interface)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.newlecture.web.data.dao;
 
import java.util.Date;
import java.util.List;
 
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.SelectKey;
 
import com.newlecture.web.data.entity.Notice;
import com.newlecture.web.data.view.NoticeView;
 
public interface NoticeDao {
    
    @Select("SELECT * FROM NOTICE_VIEW WHERE BINARY ${field} LIKE '%${query}%' LIMIT 0,10")  
    List<NoticeView> getList();
    List<NoticeView> getList(@Param("page")int page, @Param("field")String field, @Param("query")String query);
    List<NoticeView> getList(int page);
    
    @Select("SELECT * FROM NOTICE_VIEW WHERE CODE= #{code}")
    NoticeView get(String code);
    NoticeView getPrev(String code);
    NoticeView getNext(String code);
 
    
    @SelectKey(statement="SELECT MAX(CAST(CODE AS UNSIGNED))+1 CODE FROM NOTICE", before=true,keyProperty="code", resultType = String.class)
    @Insert("INSERT INTO NOTICE(CODE, TITLE, WRITER, CONTENT) VALUES (#{code},#{title},#{writer},#{content}")
    int add(Notice notice);    
    int add(String title, String content, String writer);
 
}
 
cs

명시한 엔티티 위에 어노테이션을 이용해 바로 DB에서 데이터를 집어넣을 수 있다.


xml과 작성법은 비슷하다.

주의할점은 xml에서 사용한 param1, param2 가 사용 불가능하단 것이다. 


어노테이션으로 다중 인자를 받는 경우,

@param("이름")변수형 변수명

으로 변수에 인자를 대입해 가져와야한다.


마찬가지로 서브 쿼리는 SelectKey를 이용한다.

객체로 받아오는 경우, getter에서 변수명 맨 앞 글자를 소문자로 자동으로 바꿔 sql문에 넣어준다.




지시서 작성 후, 지시서를 전달해줘야한다.


전달자는 세 놈이 가능한데, 1.Java 전역변수, 2. Tomcat, 3. Dispatcher 이다. 

보통, 트랜잭션과 보안 처리를 위해 Sprgin이용 시에는 3.디스패처를 많이 이용하므로 디스패처에게 지시서를 전달해준다.


@Servcie-Context.xml (나눈 디스패처 중 이 놈한테 넣어준다.)

1
2
3
4
5
6
7
8
9
10
11
12
    <!-- Spring에게 mybatis 지시서 주기 -->
    <!-- Mapper객체를 만들어주는 Bean -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="mapperLocations"
            value="classpath:com/newlecture/web/mybatis/mapper/*.xml" />
    </bean>
 
    <!-- 자동으로 db를 연결하고 닫아주는 Bean -->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg index="0" ref="sqlSessionFactory" />
    </bean>
cs

Bean을 이용해 Mapper객체를 생성하고, mapperLocation을 통해 위의 지시서를 전달해준다.



이제 컨트롤러단에서 사용하기만 하면 된다.


기존에 Dao객체를 일일이 생성하고 만들었지만 그럴 필요가 없어졌다.

1
2
3
4
5
6
7

    @Autowired
    private NoticeDao noticeDao; /*컨테이너가 자동으로 담아준다*/
    @Autowired
    private NoticeFileDao noticeFileDao;
cs
<기존에 DB서 데이터 가져오기위해 하던 짓>

1
2
3
4
5
6
7
8
9
10
11
    @Autowired
    private SqlSession sqlSession;
 
    @RequestMapping("notice")
    public String notice(@RequestParam(value="p", defaultValue="1")int page,
            @RequestParam(value="q", defaultValue="")String query,
            @RequestParam(value="f", defaultValue="TITLE")String field, Model model){
        
        List<NoticeView> list = sqlSession.getMapper(NoticeDao.class).getList(page, field, query);
    
cs

< Mybatis 이용시 매핑된 sqlSession 객체 이용>


Bean의 sqlSession을 이용해 매핑된 DB data를 이용하면 된다.