SQL 분할 쿼리 변환 지원 문제
문제는 페이지네이션 변경 과정에서 발생했습니다. 원래의 메서드에서 전체 결과를 가져온 후 수동으로 페이지네이션을 적용하던 로직을 startPage를 사용하여 데이터베이스에서 직접 페이지네이션된 결과를 가져오는 방식으로 수정했습니다. 아래는 간략한 코드 예시입니다:
public void foo(Entity entity){
startPage();
var list1 = myMapper.selectQuery(entity);
startPage();
var list2 = myMapper.selectQuery2(entity); // 이 문장은 SQL 변환을 지원하지 않는다는 예외를 발생시킵니다.
}
처음에는 연속적인 startPage 호출이 문제라고 생각했으나, selectQuery2를 selectQuery로 변경하여 테스트해보니 정상 작동했습니다. 이는 startPage의 연속 사용 자체가 문제가 아니라는 것을 증명합니다. 동료의 조언과 오류 메시지를 통해 selectQuery2의 SQL 문장에 문제가 있다는 것을 확인했습니다.
다음은 문제의 SQL 구문입니다:
<sql>
select DISTINCT *
FROM [19.XX.XX.XX].[데이터베이스명].[dbo].[뷰명]
</sql>
해결 방법은 from 절에서 대괄호([]) 표기법을 제거하고, 예를 들어 다중 데이터 소스 방식을 사용하여 새 Mapper를 정의하여 목표 데이터 소스에 직접 접근하도록 합니다. 이렇게 하면 SQL을 다음과 같이 수정할 수 있습니다:
<sql>
select DISTINCT *
FROM 뷰명
</sql>
참고: 테스트를 진행하지 않았으므로 MyBatis가 [ip].[데이터베이스].[스키마].[뷰] 형식을 지원하지 않는지, 아니면 단순히 대괄호 문자를 지원하지 않는지는 확실하지 않습니다.
조회 결과 병합 문제
Mapper에서 정의된 SQL 쿼리 결과는 3개의 레코드를 반환해야 하지만, 실제로는 1개의 엔티티만 반환되는 경우가 있습니다. 이는 resultMap의 정의에 문제가 있을 가능성이 있으며, 주요 원인 중 하나는 고유한 기본 키 열이 명확하게 정의되지 않았기 때문입니다. 두 가지 경우를 설명하겠습니다.
<!-- resultMap 정의 -->
<resultMap id="vo" type="ProjectCentersStatisticsVo">
<association property="entityA" javaType="domain.entityA">
<id property="id" column="id" />
</association>
</resultMap>
<select id="selectSql" resultMap="vo">
select id, name from t where xxx
</select>
첫 번째 경우, resultMap에 result 또는 열이 없고 association이나 collection만 포함되어 있으며, association 내에 id가 정의되어 있는 경우, 이 문제는 발생하지 않습니다. SQL 문에서 유일한 id가 entityA의 id에 매핑됩니다.
두 번째 경우, resultMap에 result가 정의되어 있지만 기본 키가 아닌 경우, MyBatis는 이를 기본 키로 취급하며, 값이 동일한 결과는 병합되어 마지막 레코드만 남게 됩니다.
<resultMap id="vo" type="ProjectCentersStatisticsVo">
<result property="name" column="name" />
<association property="entityA" javaType="domain.entityA">
<id property="id" column="id" />
</association>
</resultMap>
위 예시에서 resultMap에 name 열이 정의되어 있어, MyBatis는 이를 기본 키로 처리하며, 동일한 name 값을 가진 레코드는 마지막 레코드로 병합됩니다. 예를 들어 쿼리 결과가 3개의 레코드 중 두 개는 tom, 하나는 jerry라면, 최종적으로 tom과 jerry 두 개의 레코드만 반환됩니다.
세 번째 경우, 위 문제를 방지하기 위해 resultMap에 명시적으로 기본 키를 정의하는 것이 좋습니다. 기본 이름은 id입니다.
<resultMap id="vo" type="ProjectCentersStatisticsVo">
<result property="pk" column="pk" />
<result property="name" column="name" />
<association property="entityA" javaType="domain.entityA">
<id property="id" column="id" />
</association>
</resultMap>
<select id="selectSql" resultMap="vo">
select id, id as pk, name from t where xxx
</select>
위 예시에서는 resultMap에 pk 필드를 추가하여 기본 키를 표현하고, SQL에서 pk 열을 추가하여 고유성을 보장합니다. 이는 기존의 id 필드가 entityA의 id에 올바르게 매핑되도록 합니다.