728x90
반응형

Mybatis 의 강력한 sqlmapping 기능

http://www.mybatis.org/core/ko/dynamic-sql.html

 

apache project 팀에서 google code 팀으로 이사(?) 를 가게 되면서 명칭이 iBatis 에서 myBatis 로 변경 되었다.

 

version numbering 도 iBatis 시절 그대로 이어져 간다.

iBatis 최종 버전 : ibatis-2.3.4.726.zip

myBatis 시작 버전 : mybatis-2.3.5-bundle.zip

 

단순히 이름 정도의 변경일 것으로 생각 했는데 오산 이다.

google 을 조회하니 단순히 이름만 바뀐 걸로 생각하는 개발자도 있고, 실제 사용후 차이점을 정리하여 둔 개발자도 있다.

 

차이점을 간단히 정리 하면,

 

1) package 내부구조가 ibatis 와 mybatis 가 확연히 다르다.

ibatis : com.ibatis.*

mybatis : org.apache.ibatis.* 

 

* 흠. package 명에는 ibatis 를 계속 사용중이다.

 

2) sqlMap.xml 의 내부 구조가 변경 되었다.

꽤나 큰 변경점이다.

http://www.mybatis.org/core/sqlmap-xml.html

 

3) plan java framework 별로 mybatis lib 를 따로이 제공한다.

spring, Guice 용 lib 따로이 제공된다.

 

* plan java framework 랑 보다 강력히 결합되었다.

 

4) annotation 을 적극 도입하여 DAO 에 대해서 행하던 sqlMapClient DI 설정을 안해도 된다.

* spring 2.5 대 부터 annotation 이 도입되어서 설정이 매우 간편해진 것 처럼 무척 간편해졌다.

* bean id  sqlSessionFactory, sqlSessionTemplate 만 지정하면 된다.

 

5) rowHandler 가 없어졌다.(?)

* xml 및 대량 데이터 처리를 위해 사용하였던 rowHandler 가 없어졌다.(?)

* sqlMapClient 가 없어지고 sqlSession 으로 대체 되었는데, sqlSession 의 API 를 살펴보니 large data 처리용

  method 를 제공한다.

* rowHandler 가 resultHandler 로 바뀌었다.

 

http://code.google.com/p/mybatis/wiki/ResultHandlerExample

http://devday.tistory.com/entry/MyBatis%EC%97%90%EC%84%9C-resultType%EC%9D%B4-MapString-ListLong%EC%9D%BC-%EB%95%8C

 

 

    public Map<GrandFather, List<Child>> selectGrandFathersWithChildren() {
       
SqlSession sqlSession = sqlSessionFactory.openSession();
       
try {
               
class MyResultHandler implements ResultHandler {
                       
Map<GrandFather, List<Child>> grandFatherWithChildren = new HashMap<GrandFather, List<Child>>();
                               
@Override
                               
public void handleResult(ResultContext context) {
                                       
final GrandFatherWithGrandChildren complex = (GrandFatherWithGrandChildren)context.getResultObject();
                                       
if (!grandFatherWithChildren.containsKey(complex.grandFather)) {
                                                grandFatherWithChildren
.put(complex.grandFather, new ArrayList<Child>());
                                       
}
                                        grandFatherWithChildren
.get(complex.grandFather).add(complex.child);
                               
}
               
};
               
MyResultHandler handler = new MyResultHandler();
               
GrandFatherMapper grandFatherMapper = sqlSession.getMapper(GrandFatherMapper.class);
                grandFatherMapper
.selectComplex(handler);
               
               
return handler.grandFatherWithChildren;
       
} finally {
                sqlSession
.close();
       
}
   
}

 

* resultHandler 의 핵심은 ResultContext 이며 ResultContext.getResultObject() 가

  sqlMap XML 에 지정된 resultType (query 수행시 반환되는 record 의 Object 형태) 되로 Object 를  반환하여 준다.

 

 

SqlSession

As mentioned above, the SqlSession instance is the most powerful class in MyBatis. It is where you'll find all of the methods to execute statements, commit or rollback transactions and acquire mapper instances.

There are over twenty methods on the SqlSession class, so let's break them up into more digestible groupings.

Statement Execution Methods

These methods are used to execute SELECT, INSERT, UPDATE and DELETE statements that are defined in your SQL Mapping XML files. They are pretty self explanatory, each takes the ID of the statement and the Parameter Object, which can be a primitive (auto-boxed or wrapper), a JavaBean, a POJO or a Map.

<T> T selectOne(String statement, Object parameter)
<E> List<E> selectList(String statement, Object parameter)
<K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey)
int insert(String statement, Object parameter)
int update(String statement, Object parameter)
int delete(String statement, Object parameter)

The difference between selectOne and selectList is only in that selectOne must return exactly one object or null (none). If any more than one, an exception will be thrown. If you don't' know how many objects are expected, use selectList. If you want to check for the existence of an object, you're better off returning a count (0 or 1). The selectMap is a special case in that it is designed to convert a list of results into a Map based on one of the properties in the resulting objects. Because not all statements require a parameter, these methods are overloaded with versions that do not require the parameter object.

The value returned by the insert, update and delete methods indicate the number of rows affected by the statement.

<T> T selectOne(String statement)
<E> List<E> selectList(String statement)
<K,V> Map<K,V> selectMap(String statement, String mapKey)
int insert(String statement)
int update(String statement)
int delete(String statement)

Finally, there are three advanced versions of the select methods that allow you to restrict the range of rows to return, or provide custom result handling logic, usually for very large data sets.

<E> List<E> selectList (String statement, Object parameter, RowBounds rowBounds)
<K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowbounds)
void select (String statement, Object parameter, ResultHandler handler)
void select (String statement, Object parameter, RowBounds rowBounds, ResultHandler handler)

The RowBounds parameter causes MyBatis to skip the number of records specified, as well as limit the number of results returned to some number. The RowBounds class has a constructor to take both the offset and limit, and is otherwise immutable.

int offset = 100;
int limit = 25;
RowBounds rowBounds = new RowBounds(offset, limit);

Different drivers are able to achieve different levels of efficiency in this regard. For the best performance, use result set types of SCROLL_SENSITIVE or SCROLL_INSENSITIVE (in other words: not FORWARD_ONLY).

The ResultHandler parameter allows you to handle each row however you like. You can add it to a List, create a Map, Set, or throw each result away and instead keep only rolled up totals of calculations. You can do pretty much anything with the ResultHandler, and it's what MyBatis uses internally itself to build result set lists.

The interface is very simple.

package org.apache.ibatis.session;
public interface ResultHandler {
  void handleResult(ResultContext context);
}

The ResultContext parameter gives you access to the result object itself, a count of the number of result objects created, and a Boolean stop() method that you can use to stop MyBatis from loading any more results.

 

 

6) myBatis 마이그레이션 가이드 이다.

 

From iBATIS 2.x to MyBatis : http://code.google.com/p/mybatis/wiki/DocUpgrade3

 

7) Interceptor

 

http://stove99.tistory.com/34

 

===============================================================================================

펌글 : http://rinn.kr/49

 

 

iBatis에서 myBatis로 개요 및 변경점


원래 마이그레이션 포스팅부터 다룰려고 했으나.. 이게 공식위키에서는 엄청 쉽게 이야기 한것과 달리 그냥 라이브러리 교체 정도로 끝나는게 아니라서 일단 바뀐 용어와 개요부터 정리를 좀 해야 할 필요성을 느꼈다. 

myBatis로 바뀌면서 기본 용어들이 조금씩 차이가 나기 때문에.. 주의가 필요하다. 

기존 SqlMapConfig은 Configration로 변경되었고 sqlMap은 mapper로 변경되었다. 다른 용어들이 변경된 상황이나.. 내부적으로 사용하는 것들(ex. isEqual 을 아에 if 로 바꾼거라던지)을 보면 좀더 범용적으로 알아보기 쉽게 바꿔가는 것을 목표로 가고있는 듯 하다. 
익숙해지면 아무래도 코드 읽기도 만들기도 쉬울것으로 보인다. 

큰 변화중 하나는 자바 애노테이션을 사용해서 xml을 사용하지 않고 모든것을 자바로만 할수 있게 되었다
물론 Configration.xml 도 자바에서 직접 DataSource,  Environment 등을 선언해서 클래스화 시킬수 있다. xml 스트링으로 설정값등을 저장해야 한다는 것에 부담을 느꼈다면 좋은 변화라고 할수 있겠다.

주의할점은 xml로 Configure를 만들고 환경변수와 property를 클래스로도 만들었다면.. 클래스쪽이 나중에 읽어지게 되서 xml로 되어있는 세팅이 자바 클래스에서 선언해놓은것으로 덮어써지게 된다. 혼란을 줄수 있으니 한가지 방법만으로 프로젝트를 구성하는것이 좋을것이다. 

그리고 Configuration configuration = new Con.... 형식으로 선언을 하고 나서는 mapper도 xml이 아니고 configuration.addMapper(UserMapper.class) 형식으로 추가 해야 하기 때문에 어느쪽으로 할것인지 확실하게 결정을 하고 나서 진행해야 한다.

네임스페이스
 방식도 변경되었는데.. sqlMap 파일별로 줄여놓은 이름을 사용했다면 이제 풀경로로 사용하게 된다. 공식 설명서에서는 혼란을 줄이고 어떤것이 호출되는지 정확하게 알수 있으니 좋다라고 해놨지만 아무래도 길어지니 쓰기에 불편하기는 하다.. 
기존에 <sqlMap namespace="User"> 이렇게 쓰던것을 
<mapper namespace="myBatis.mapper.UserMapper"> 이렇게 풀 경로로 쓰게 된다.

실제 자바쪽에서 호출할때도 
1 list = session.selectList("myBatis.mappers.UserMapper.getUserList");

이렇게 길게 호출 하게 되는데.. 그냥 string 이라서 입력이 여간 불편하다.

이런 경우에 위에서 이야기한 자바 애노테이션 (@Select)을 사용해서 mapper 파일을 xml이 아니고 자바로 만들어놓으면 코드힌트까지 사용해서 편하게 쓸수있다.
1 UserMapper mapper = session.getMapper(UserMapper.class);
2 list = mapper.selectUserList();

권장사항은 xml 이라고 되어있었던거 같은데.. 편하기는 자바쪽이 편한 구조랄까..;;




기본 용어

SqlSessionFactory :  SqlMapClient가 SqlSessionFactory로 변경되었다. 어플리케이션과 같은 라이프사이클을 가지게 된다. 한번만 생성되면 되므로 Manager 클래스에서 싱글톤으로 구현하면 된다.

SqlSessionFactoryBuilder : 환경 값(디비 및 트랜잭션 설정등..)을 읽어와서 SqlSessionFactory 인스턴스를 만들어준다. 기존의 SqlMapClientBuilder 대신 사용된다.

1 String resource = "org/mybatis/example/Configuration.xml";
2 Reader reader = Resources.getResourceAsReader(resource);
3 sqlMapper = new SqlSessionFactoryBuilder().build(reader);

SqlSession : SqlSessionFactory 에서 세션을 하나씩 열어서 개별 쓰레드 별로 사용한다. 세션을 열고나서 실제 쿼리를 수행하게 된다. 하나의 리퀘스트에 하나의 세션을 가지게 되고 사용후에는 꼭 닫아줘야 한다. 

mapper : 기존의 sqlMap이 변경된 것이다. 실제 쿼리들이 들어있게 되고 위의 SqlSession을 열어야 호출할 수 있다. method scope를 가지게 되고 해당 메서드가 사용되고나면 사라진다. 별도로 닫거나 할 필요는 없고 SqlSession이 함께 관리 한다. 
1 SqlSession session = sqlSessionFactory.openSession();
2 try {
3     UserMapper mapper = session.getMapper(UserMapper.class);
4     // do work
5 finally {
6     session.close();
7 }


변경되거나 추가된 속성들

기존에 조건에 따라 변하는 쿼리를 만들기 위해서 사용되던 태그들이 변경되었다. 조금더 직관적으로 바뀌었고 해당상황(Update, Select)등에 맞춰서 사용할 수 있는 태그들도 추가되었다. 

parameterMap은 더이상 사용하지 않게 되었다. parameterMap과 parameterClass 대신 parameterType 하나로 사용한다.
resultMap은 여전히 남아있지만 resultClass 는 resultType 으로 변경되었다.
parameterType과 resultType에는 기본형(int, byte, .... )부터 클래스 명까지 기존처럼 사용할 수 있다.

기존에 procedure를 호출하기 위해 사용하던 <procedure>가 사라지고 statementType 속성이 생겼다. PREPARED, STATEMENT, CALLABLE 중에 하나를 선택할 수 있고 기본값은 PREPARED이다. 

파라미터를 매핑하기위해서 사용하던 #var# 형태는 #{var} 로 바뀌었다. $var$ 역시 ${var} 형태로 사용하면 된다.
  
참고) #{var}와 ${var}의 차이는 prepredStatement의 파라미터로 사용할 것인가.. 그냥 String 값으로 때려박을것인가 하는 것이다. order by 같은 경우에 사용하기 위해서는 order by ${orderParam} 처럼 사용해야 한다. 이 방법을 사용하는 경우 myBatis가 자체적으로 쿼리의 적합성여부를 판단할 수 없기 때문에 사용자의 입력값을 그대로 사용하는 것보다는 개발자가 미리 정해놓은 값등으로 변경하도록 해서 정확한값이 들어올수 있도록 해야 한다. 


sqlMap쪽에서 사용하던 typeAlias가 sqlMap이 바뀐 mapper 에서 사용되지 않고 Configration 파일에서 정의하도록 변경되었다. 

<typeAliases>
    <typeAlias type="vo.UserVO" alias="User"/>
</typeAliases>

Configration 파일에 위의 형식처럼 Aliase를 정의하면 전체 mapper 에서 사용할 수 있다. 



Dynamic Statement의 변화

<isEqual> , <isNull> 등의 구문이 <if>로 통합되었다. 이전보다는 확실히 직관적으로 쓸수 있을듯 하다. 
<if test="userID != null"> 형태로 간단하게 사용할 수 있다. (스트럿츠2에서 사용하는 형태 처럼 보이는데..;;)

<dynamic > 형태로 해서 where 조건절이나 and , or 를 동적으로 만들던것이 <where>나 update에서 사용할 수 있는 <set> 등으로 변경되었다. 

<select id="getUserList" resultType="User>
    SELECT * FROM TR_USER
        <where>
            <if test="isAdmin != null">
                authLevel = '1'
             </if>
          </where>
</select>

trim, foreach 태그가 새로 추가 되었다. 
trim은 쿼리를 동적생성할때에 쿼리를 연결하기 위해서 컴마(,)를 사용한경우 마지막항목이 조건을 만족하지 못해서 생성된 쿼리 끝에 컴마가 붙어있다던가 하는 경우에 잘라낼 수 있다. 
foreach는 반복적인 항목을 동적으로 넣을때 사용할 수 있다. ( ex. where 조건절에서 in 을 사용하는 경우)

공식홈페이지의 위키에 기존 iBatis를 myBatis로 바꿀때 확인해야 할 부분들이 있으니 꼭 참고 하자.
http://code.google.com/p/mybatis/wiki/DocUpgrade3

ps.
지금 있는것도 못하고 있는데 항상 새로운 버전이 쏟아지니 미칠꺼같다 -ㅅ-;;
Flex4도 정식으로 손대보지 못했는데 5 소식이라니.. ㅠㅠ

 

728x90
반응형
블로그 이미지

nineDeveloper

안녕하세요 현직 개발자 입니다 ~ 빠르게 변화하는 세상에 뒤쳐지지 않도록 우리모두 열심히 공부합시다 ~! 개발공부는 넘나 재미있는 것~!

,