iBATIS 강좌 <1> Complex Type Property 사용하기
<2회> Discriminator 를 이용하여 상속 구현하기
iBatis는 객체지향 개발을 쉽게 할 수 있도록 도와주고 다양한 유용한 기능을 갖추고 있 때문에, 알아두면 업무에 유용하게 쓰일 수 있을 것입니다.
일반적으로 많이 사용 되고 있는 iBATIS의 기능을 예제를 통해 설명한 본 글을 통해 많은 개발자분들이 도움을 받으실 수 있었으면 합니다.
‘iBATIS’는 국내 java 개발에서 가장 많이 사용되고 있는 database 연동 framework이다.
iBATIS는 훌륭한 SQL mapper로서 query를 코드와 분리하여 쉽게 사용하고 관리할 수 있도록 해준다. 하지만 전반적인 Java의 database 연동 기술이 ORM(Object-Relational Mapping) 기술을 기반으로, 표준 스펙인 JPA(Java Persistence API)를 중심으로 발전하고 있고, 해외에서는 이를 더 많이 사용하고 있는 것과 비교할 때, 국내 상황은 좀 차이가 있다.
이는 마치 국내 웹에서 ‘ActiveX’가 특별히 많이 사용되고 있는 것과 비슷하다 할 수 있는데, 국내에서 특별히 iBATIS가 많이 사용되는 데에는 아래와 같은 이유가 있을 수 있다.
iBATIS는 이러한 설계/개발에 적합하다.
- 객체지향 설계보다는 query에 더 익숙한 개발자들에게 iBATIS는 ORM기술에 비해
learning curve 가 짧고 쉽게 사용할 수 있다.
- Query를 직접 작성하지 않는 ORM 기술에 비해 성능에 대한 ‘염려’를 줄여준다.
(ORM기술이 꼭 성능이 떨어진다는 얘기가 아니다.)
사실 위의 내용들을 ‘단점’이라고 할 수 없고, iBATIS가 나쁘다고 할 수도 없다. 단지, database와query를 위주로 개발하면서 ‘객체지향’ 개발에서 멀어지는 것이 문제이다.
여기서는 일반적으로 많이 사용되고 있는 dynamic query 기능 외에 iBATIS를 좀 더 객체지향 적으로 사용할 수 있도록 해주는 iBATIS의 두 가지 기능을, 예제를 통해서 살펴보려고 한다.
한가지는 ‘complex type property에 대한 지원’(사용자가 정의한 class 를 property로 가지고 있는 경우),
그리고 다른 하나는 discriminator를 이용한 ‘상속 class처리’에 대한 지원이다.
흔히 많이 사용하는 ‘고객-주문-주문item-상품’ 관계를 예를 들어보자.
간략하게 database table 은 아래와 같이 구성될 수 있을 것이다.
이러한 구조를 자바 class로 구현할 때, 아래와 같이 table 의 구조를 그대로 옮겨놓는 식으로 개발을 할 수 있을 것이다.
이번에는 객체지향 설계 관점으로 접근해보자. 객체지향 시스템은 수많은 객체들간의 관계로 이루어진 하나의 네트웍이라고 할 수 있고 하나의 객체를 통해서 관련된 다른 객체의 정보들을 얻을 수 있다. (도메인 주도 개발방식에서는 이처럼 거대한 객체들의 관계 사이에서 접근을 시작하는 객체를 ‘Entry Point’ 라고 부른다.)
여기서는 Order를 Entry Point로 삼도록 하겠다. 즉, Order 객체에 얻게 되면 그와 관련된 Customer 객체, Order Item 객체들 그리고 Order Item 객체가 관계를 맺고 있는 Product 객체의 모든 정보들을 가져올 수 있게 된다는 얘기이다. 이를 Class Diagram 으로 표현하면 아래와 같을 것이다.
관련 코드는 아래와 같이 달라진다.
과연 이러한 변화가 Business 코드에 어떤 영향을 미칠까?
한번 어떤 고객의 전체 주문 액을 구하는 기능을 살펴보자. 처음과 같이 테이블 구조에만 맞춘 클래스의 경우는 아래와 같이 구현이 될 것이다.
Dao 수행 3회
그럼 이번엔 아래 객체지향 시스템에서처럼 Entry Point 를 통해 모든 정보를 얻을 수 있는 경우를 보자.
Dao 수행 한 번으로 간소화 됨
두 번째 코드는 DAO 접근이 한번으로 줄어들면서 코드가 훨씬 간결해 졌다.
iBATIS에는 두번째와 같은 방식의 개발을 지원해주는 기능이 있는데 이는 사용자의 Class로 된 property에 대한 지원이다. 이는
Result map 안에서의 select 문 사용하여 다른 query를 실행하여 결과를 얻는 방법
Join을 사용하여 한번에 결과를 얻는 방법
을 통해서 가능하다.
먼저 Result map 안에서 select 문을 사용하는 경우 iBATIS sql map 은 아래와 같을 수 있다.
위에서 보면 Order 를 조회하게 되면 해당 Order와 관련된 Customer 그리고 OrderItem collection도 함께 조회하여 결과값을 return 되게 된다.
이번에는 Join 을 사용하고 iBATIS를 통해 정리된 결과 값을 받는 sqlmap이다.
여기서 살펴볼 것은, ‘orderItems’ property 와 같이 collection 의 경우는 “groupBy” 옵션을 통해 iBATIS가 알아서 collection 객체를 구성해주게 된다. 즉, customerId 에 의해 조회되는 Order가 여러 건이 될 수 있고, 또 각각의 Order 가 여러 개의 OrderItem을 가지게 될텐데, iBATIS는 ‘ORDER_ID’를 기준으로 OrderItem들은 grouping 하여 Order list 구성한 후 return 하게 된다
두 번째 방식의 경우 쿼리가 한번만 실행되므로 어떤 면에서는 성능 향상을 가져올 수 있다. 하지만 첫 번째 방식이 무조건 성능이 안 좋다고 할 수 없는데, 이는 ‘Lazy loading’ 이라는 기능이 있기 때문이다.
이는 iBATIS에 기본적으로 적용되어 있는 기능인데, 첫 번째 경우와 같이 result map 을 통해서 다른 query를 수행하는 경우에, 실제로 처음부터 모든 query가 다 실행되는 것이 아니라, 해당 property에 접근하는 순간에 그 query가 수행되는 것이다.
즉, Order 를 조회했어도, 만일 customer property에 접근을 하지 않는다면 실제로 Customer 조회 query는 실행되지 않는다.
이와 같이 iBATIS는 비록 ORM은 아니지만, ORM이 제공하는 기능들을 여러 가지 방식으로 지원하므로써 객체지향 개발을 쉽게 할 수 있도록 도와준다.
다음 번에는 객체지향 개발에서 많이 사용하게 되는 ‘상속’을 iBATIS로 어떻게 적용할 수 있는지 살펴보겠다.
* 첨부한 eclipse project는 spring과 maven기반으로 되어 있습니다. Enterprise 버전 eclipse에m2eclipse plugin이 설치하여 직접 테스트해 볼 수 있습니다.
출처 - http://blog.skcc.com/90