<select id="selectCategoryCount" parameterType="java.util.Map"  resultType="keyword">

                           select

                          

                           log_date as logDate,

                           <choose>

                                        <when test="@org.apache.commons.lang.StringUtils@isEmpty(siteCode)">

                                                     '전체' as siteCode,

                                        </when>

                                        <otherwise>

                                                     site_code as siteCode,

                                        </otherwise>

                           </choose>

                           <choose>

                                        <when test="@org.apache.commons.lang.StringUtils@isEmpty(firstKeyName)">

                                                     '전체' as keyName,

                                        </when>

                                        <otherwise>

                                                     category as keyName,

                                        </otherwise>

                           </choose>

                           sum(count) as value

                           from category_count

                           where log_date &gt;= #{startLogDate}

                           and log_date &lt;= #{endLogDate}

                           <if  test="@org.apache.commons.lang.StringUtils@isNotEmpty(siteCode)">

                                        and site_code = #{siteCode}

                           </if>

                           <if  test="@org.apache.commons.lang.StringUtils@isNotEmpty(firstKeyName)">

                                        and category = #{firstKeyName}

                           </if>

                           group by log_date

             </select>


저작자 표시 비영리 변경 금지
신고
Posted by 용식

처음에 이 관계를 몰랐는데..

좀 가지고 놀다보니까.. 아~ 하는 부분이 있어서 정리..


테스트를 위한 기본 셋팅


1. Dual.java


2.DualMapper.xml

 

3. DualMapper.java


mapper xml을 보면 namespace가 있는데 이 namespace를 mapper interface의 이름과 맞춰주면 xml의 설정과 인터페이스에 선언한 설정을 같이 사용 할 수 있게 된다. 그리고 제약도 같이 적용이 된다.


즉 위의 상황에서 DualMapper의 selectAll2 메서드를 selectAll로 변경하게 되면 이미 mapper.xml에서는 selectAll이라는 id가

존재하기 때문에 에러가 발생한다.


결국, Mapper Interface의 메서드는 id로 @select 어노테이션은 select구문으로, @ResultMap 어노테이션은

mapper.xml에서 설정한 resultmap의 id를 가르킬 수 있게 된다.


위와 같이 설정한 경우 아래의 구문은 모두 사용가능하다.


저작자 표시 비영리 변경 금지
신고
Posted by 용식

mybatis 셋팅 후 수십만건의 row를 가져와야 할 경우.. 

혹은 단순히 Collection 타입으로 result를 받아야하는 상황이 아니라

Map<String, List<String>> 이런 형태일 경우 ResultHandler를 사용 할 수 있다.


내 경우는 수십만건을 한번 읽어서 가져와야 하는 케이스였는데..

단순히 SqlSession으로부터 selectLIst를 하면 전체 row를 메모리에 올리기 때문에

어느정도 수준을 넘어가면 OOM이 발생한다.


DB에 있는 수십만 row를 읽어서, 파일로 떨궈내거나 할 때.. 쓰면 되겠다.


1. mapper xml을 사용하는 경우



2. mapper interface를 사용하는 경우


저작자 표시 비영리 변경 금지
신고
Posted by 용식
이전 글에서 Mapper를 MapperFactory라는 클래스를 통해 생성하여
구현하였었습니다.

앞에서도 말씀드렸지만
이전 예제는 Mapper의 return type이 고정되어 버린다는
단점이 있었습니다. 이 부분을 Generic을 사용하여 수정해보려고 합니다.

기본적으로 앞서 구현했던 Select 어노테이션과 User클래스, 그리고 User를 Select 할 수 있는 UserMapper외에 
Depart 도메인에 대한 Mapper를 추가하겠습니다.

  Depart.java
DepartDataMapper.java

Depart와 Depart를 Select하는 Mapper입니다.

그 다음 이 Mapper를 대신하여 수행 될 ProxyHandler입니다.


MapperProxyHandler.java

보시면 아시겠지만
리턴되어야하는 타입이 User와 Depart 두개로
이를 구분하기 위하여 내부적으로 If문을 사용하고 있습니다.

지금은 Type이 2개뿐이지만 실제로는 이런식으로는 구현 자체가 어려울 것 입니다.
이 부분은 Reflect를 사용하여 IF문을 쓰지 않고도 Return Type을 구분 하도록 할 수도 있습니다.



MapperProxyHandler.java (개량형)

* 위 예제는 풍주형님(http://pungjoo.tistory.com/)께서 댓글로 알려주신 내용입니다. 

위에 보시면 메서드명을 인위적으로 만들어서 setter 메서드를 relfect로 실행하여
값을 셋팅하고 있습니다.

위 예제에서 메서드명을 DB ResultSet의 MetaData로부터 컬럼명을 읽어서
만들어 낼 수도 있고, 스프링프레임워크등을 사용하면 BeanProperty와 같은 라이브러리를
사용하여 좀 더 쉽게 사용 할 수도 있습니다.

아무튼 위와 같이 ProxyHandler가 작성되고나면 이제 MapperFactory를 작성해야 합니다.


MapperFactory.java
이렇게 Generic을 사용하면 이 Factory가 여러 종류의 Mapper를 생성 할 수 있게 됩니다.
아래 예제처럼요..


Test.java

이제 MapperFactory는 더 이상 Mapper의 Type에 종속적이지 않습니다.
보시면 하나의 Factory에서 여러 종류의 Mapper를 만들어내고 있습니다.

이렇게해서 mybatis에서 사용되는 mapper를 proxy api를 활용하여
구현해보았습니다.당연히 실제 Mybatis 내부적으로는 훨씬 많은 validation로직과 기타 예외에 대한 처리들이
구현되어 있을 것 입니다.

단순히 흉내내기에 불과하지만
proxy, annotation, generic등 여러 종류의 api에 대해서도
한번 훑어 볼 수 있는 예제라고 생각합니다.


저작자 표시 비영리 변경 금지
신고
Posted by 용식
TAG mybatis, proxy
우선 앞선 포스트에서 말씀드렸듯이  http://pungjoo.tistory.com/17  이곳에서
Proxy API의 사용방법과 내용에 대해서 먼저 읽어주세요.

위 내용을 기반으로 예제가 작성됩니다.

myBatis에서 사용하는 Mapper를 한번 비슷하게 구현해보겠습니다.

우선 Mapper에서 쿼리문을 지정하기 위해 사용될 Annotation을 만들어야 합니다.
실제 MyBatis와 동일하게 Select라는 이름을 사용하겠습니다.

  Select.java

RetentionPolicy가 RUNTIME으로 되어 있으므로, 이 Annotation은 JVM이 실행되는 동안 사용가능하게 됩니다.
그리고 query라는 변수를 통해 위 Annotation은 쿼리식을 가지고 있을 수 있게 됩니다.

그 다음 필요한 클래스는 Domain의 역할을 할 User 클래스입니다.
간단하게 이름과 주소 필드만을 가지고 있습니다.
이 예제에서는 실제로 DB와 연결하여 데이터를 가져 올 것이 아니고
구현되는 형태만을 보기 위함이기 때문에 실제 데이터가 이 Domain에 들어가지는 않을 것 입니다.


User.java

이번에는 Mapper입니다. Mapper는 인터페이스이며 메서드에는 해당 메서드가 호출 될 때 실행 될
쿼리문이 설정되어 있습니다.


UserDataMapper.java

그리고 위 Mapper는 ProxyHandler를 사용하여 Proxy Instance로 사용되고 이를 통해
위 Mapper의 selectUser 메서드를 실행하면 User 클래스에 데이터가
담겨 return됩니다. 구현 클래스도 없는데말이죠. 그러면 실제로 그 일을 하게 될 ProxyHandler를 
살펴보겠습니다.



MapperProxyHandler.java

앞선 예제들과 마찬가지로 InvocationHandler를 구현하고 있습니다.
Proxy API에 의해서 위 Mapper의 getUser 메서드가 호출 될 경우 이 MapperProxyHandler의 invoke 메서드가
실행됩니다. 

이 메서드의 파라메터 중 Method 파라메터로 getUser가 넘어오게 되고 이 메서드로부터 Select Annotation을
얻어낼 수 있습니다. 그리고 그 Select Annotation으로부터 쿼리를 받아 실행을 하는 로직이 이 invoke 메서드안에
구현되게 됩니다.

위 Handler를 통해 메서드를 실행하게 하려면 Proxy.newProxyInstance 메서드를 사용해야합니다.

이 부분을 MapperFactory라는 이름으로 해당 Mapper를 가져 올 수 있도록 하겠습니다.


MapperFactory.java

그리고 최종적인 실행되는 모양은 아래와 같습니다.


Test.java

MapperFactory를 통해 UserDataMapper를 얻어내고
이 때 내부적으로는 Proxy를 사용 할 준비를 합니다. 그리고 selectUser 메서드를 실행 할 때 위에 작성된
invoke 메서드를 실행하게 됩니다.

이 예제에서는 한가지 (사실은 매우 여러가지...) 단점이 있는데요
MapperFactory가 한번에 하나의 Mapper만 return 할 수 있다는 것 입니다. 예를 들어 부서를 Select하는
Mapper가 있다면 리턴되는 Domain의 Type이 User가 아니게 되고, 따라서 Mapper도 바뀌어야 한다는
것 입니다.

물론 캐스팅을 사용하면 여러 Mapper를 사용하도록 할 수도 있습니다. 

이 다음 포스트에서는 위 MapperFactory를 제네릭을 사용하여
범용적으로 여러 Mapper를 캐스팅을 하지 않고도 사용 할 수 있도록 해보려고 합니다.
저작자 표시 비영리 변경 금지
신고
Posted by 용식
TAG mybatis, proxy
얼마전에 Ibatis 3.0에 해당되는 mybatis가 나왔었습니다.
xml에 쿼리를 작성하지 않고도 어노테이션과 인터페이스만으로 쿼리를 실행하고 결과를 받을 수 있게 기능이
추가 되었는데요

대략적인 API의 사용 예를 보면 아래와 같습니다.

  MyBatis에서 Mapper의 사용예제
UserDataMapper.java 위와 같이 Mapper 인터페이스에 어노테이션을 활용하여 쿼리문을 넣어 놓으면
별도의 구현클래스를 만들지 않아도, SqlSession으로부터 Mapper를 얻어와서 

바로 쿼리를 실행하고 그에 따른 결과를 받을 수 있게 되어있습니다. 물론 SqlSession에는 MyBatis에서 사용하기 위해서 기본적은 DB 관련 정보들을 properties로부터 읽어와서 사용하도록 되어있을 것 입니다. 어떻게 아무런 구현체도 없이 저렇게 사용이 가능 할 것일까요? 먼저 아래의 포스트를 읽어주세요. 


http://pungjoo.tistory.com/17


GS에서 근무 할 때 저에게 많은 것을 가르쳐주셨던 풍주형님의 글입니다. 여전히 제목에는 작성중이라고 되어있지만 이 글은 저 상태만으로도 Proxy에 대해서 간단한 예제를 통해 많은 것을 보여주는 글입니다. 이 글을 보고 이해가 잘 되지 않는 예제를 몇 번씩 실행해보고 고쳐보면서 내용을 익혔었는데 MyBatis의 Mapper를 보고 MyBatis에서 Proxy를 사용하는 것이 아닐까? 라는 생각이 들었었습니다. Mapper에는 쿼리와 Return Type만이 선언 되어있습니다. 아무런 구현체가 없습니다. 그렇다면 이 Mapper의 역할을 다른 클래스가 대신 하도록 되어 있는 것이 아닐까 라는 생각과 풍주형님께서 작성하셨던 글의 예제를 좀 활용하면 비슷하게 구현을 해 볼 수 있게다 싶었던거죠.. ㅎㅎ 앞으로 2개의 포스트를 거쳐서 Proxy를 사용하여 MyBatis의 Mapper를 흉내내보려고 합니다. 본래 팀내 후배들에게 가르쳐주려고 만들었던 예제들인데요 그 예제들에 대해서 설명을 하는 형태로 글을 작성하려 합니다. 


저작자 표시 비영리 변경 금지
신고
Posted by 용식
TAG mybatis, proxy
Ibatis의 버전이 3.X로 올라가면서
구글 코드에 새 둥지를 틀고 MyBatis라는 이름으로 개명하였다.
이번에 내부적으로 혼자 프로토타입을 만들 것이 있어서, Persistence 레이어의 프레임웍 땜시
항상 고민하다가...
간단하게 쓰면 되는데 아이바티스니 하이버네이트 이런거 쓰자니
너무 번거롭고...
간단하게나마 만들어 쓰자니 것도 좀 번거로웠던차에..
몇몇 간단한 설정으로 사용 할 수 있다고 하여 한번 시도해보았다..

어노테이션과 인터페이스만으로
사용이 가능한데.. 아마 프록시를 사용하는 것 같다..

일단 xml 설정은 필요 없고
SqlSession을 리턴해주는 녀석을 만든다.

MyBatis에서는 기본적으로 SqlSession이라는 놈을 사용해서 dml을 수행하는 것 같다..
하이버네이트같네.. 이름이 꼭...

그리고, Domain 클래스를 만든다.

그냥 DTO 역할을 하는 클래스이다.

이제 Mapper인터페이스를 만들어야 하는데, 어노테이션을 사용하여 간단한 SQL문을
바로 쓰고 실행 할 수 있게 되어 있다.

Mapper에 대한 Full Description은 
http://java.dzone.com/articles/getting-started-ibatis-mybatis-0
여기에 잘 나와있는 듯 하다.

난 그냥 간단하게 쓸거라 아래와 같이 작성하였다.

다른건 뭐 다 알겠는데
insert를 할 때 다량의 데이터 insert를 하는 방법을 잘 못 찾아서...
파라메터로 List<Object>를 넘기니 List 자체를 파라메터 Object로 인식하고
SQL문의 바인딩 변수에 전부 null이 들어가는 현상이 발생하였다.

그리고, 또 하나는 nullable 변수에는 위 처럼 jdbcType을 지정해 줘야 한다는 것...

일단 위와 같이 작성하고..DAO에서 다량의 데이터 insert를 처리해주기로 하였다.

insert 부분을 보면 openSession시에 ExecutorType.Batch 타입으로 Session을 get하도록 해주었다.

1천만건의 데이터를 넣어줘야 하는데, List에 1천만건을 넣어서 파라메터로 넘겨주면
out of memory 에러가 발생하므로, 바깥에서 List가 5만건이 될 때마다 dao.insert 문을 호출하여
insert하도록 해주었다.

로컬에서 돌려보니 10150000건 insert에 13분 정도 소요된다..

설정이 간단하여, 프로토타입 재빨리 개발 할 필요가 있을 때
유용하게 사용하게 될 것 같다..

근데 대략.. 요즘 나오는 자바 프레임워크들은
진짜 설정이나 이런게 너무 많고 복잡해서.. 좀 접근하기가 힘들다 -_-;

유연성 <-> 간편함

이 두가지를 모두 만족하는 그런 패턴은 없을까...
 
저작자 표시 비영리 변경 금지
신고
Posted by 용식
TAG mybatis