고객사 업무를 하다보니 MyBatis를 더 많이 접하고있다.
일반적으로 SQL 매핑 프레임워크 관련 트렌드는 JPA, Hibernate가 압도적이긴 하나 아직까지 한국에서 MyBatis 비율이 높은데에는 정부의 표준 프레임워크가 MyBatis를 채택하고 있는게 한 몫하지 않나싶다.
무튼 이러한 MyBatis 사용시 SQL이 동적으로 작성되도록 할 때 유용한 <bind>에 대해 간단히 정리해보았다.
바로 예제 코드부터. (✭ 실무에서 유용할 활용법은 예시 3번)
예시 1
<select id="getUsersByAge" resultType="User">
<bind name="maxAge" value="age + 5" />
SELECT * FROM users
WHERE age BETWEEN #{age} AND #{maxAge}
</select>
예시 1
여기서 <bind> 태그는 ‘maxAge’라는 변수를 만들고 있다. 파라미터로 받은 ‘age’에 5를 더해서 5살 더 많은 사람들까지 조회하는 쿼리를 만들었다. 사실 위 예제는 괜히 코드량만 더 길게 만든 감이 있는 예제이지만, bind 태그를 잘 활용하면 다음과 같은 이점을 얻을 수 있다.
코드 재사용성 ↑
복잡한 로직 분리, SQL 가독성 ↑
두 개의 예시를 더 살펴보자
예시 2
<select id="searchProducts" resultType="Product">
<bind name="searchPattern" value="'%' + keyword + '%'" />
SELECT * FROM products
WHERE name LIKE #{searchPattern}
OR description LIKE #{searchPattern}
</select>
예시 2
이 예제에서는 검색 키워드를 포함하는 패턴을 만들고 있다. ‘searchPattern’이라는 변수에 앞뒤로 ‘%‘를 붙여서 부분 일치 검색이 가능하도록 만들어졌다.
이렇듯 <bind> 태그는 SQL 실행 전 java 코드로 선 처리된다는 점에서 의미가 있다. 따라서 다음에 소개할 예시와 같이 응용해볼 수 있다.
**✭ **예시 3 ✭
예시 3-1)
<sql id="autospFilterSp">
<bind name="purchaseCdListIncludedInSp" value="@com.a.b.c.common.constant.d.EnumPurchaseType@getPurchaseCdListIncludedInSp()"/>
AND IF(ATSP.SP_YN = 'Y', RCM.OFFR_TYP_NM NOT IN (<foreach collection="purchaseCdListIncludedInSp" item="item" separator=",">#{item}</foreach>), TRUE)
</sql>
예시 3-1
예시 3-2)
@Getter
public enum CmmtOptiSpPurchaseType {
/** ComputeSavingsPlans */
COMPUTE("ComputeSavingsPlans", "Compute",
"AmazonEC2",
"AmazonECS",
"AmazonEKS",
"AWSLambda"
),
/** EC2InstanceSavingsPlans */
EC2_INSTANCE("EC2InstanceSavingsPlans", "EC2 Instance",
"AmazonEC2"
),
/** SageMakerSavingsPlans */
SAGEMAKER("SageMakerSavingsPlans", "SageMaker",
"AmazonSageMaker"
)
/** 코드 */
private final String cd;
/** 이름 */
private final String nm;
/** 서비스 코드 집합 */
private final Set<String> svcCdSet;
CmmtOptiSpPurchaseType(String cd, String nm, String... svcCdArray) {
this.cd = cd;
this.nm = nm;
this.svcCdSet = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(svcCdArray)));
}
...생략...
public static List<String> getPurchaseCdListIncludedInSp() {
return Arrays.asList(EnumPurchaseType.COMPUTE.getCd(), EnumPurchaseType.EC2_INSTANCE.getCd());
}
}
예시 3-2
설명:
com.a.b.c.common.constant.d 패키지 경로의
EnumPurchaseType라는 이름의 enum에서getPurchaseCdListIncludedInSp메서드를 실행 후 그 return값을purchaseCdListIncludedInSp변수로 설정 ※ return값: [“ComputeSavingsPlans”, “EC2InstanceSavingsPlans”]MyBatis
<foreach>를 통해 동적 SQL문 구현
AND IF(ATSP.AUTOSP_YN = 'Y', RCM.OFFR_TYP_NM NOT IN ('``ComputeSavingsPlans’, ‘EC2InstanceSavingsPlans’)앞선 두 예시는 뭔가 쿼리로도 해결할 수 있는 내용을 더 번잡하게 만든 것 같은 느낌이 든다.
하지만 세 번째 예시 처럼,
<bind> 태그는 이미 구현되어있는 Enum 코드를 재사용하거나 method return 값을 통해 SQL을 동적으로 생성할 수 있고, 하드코딩과 중복코드를 피할 수 있다는 점에서 가치가 있다.