MyBatis 3.5 소스 코드 노트: Executor, 동적 SQL, 캐시 경계
기준: MyBatis 3.5.x, Spring Boot 3, Java 17+.
1. SqlSession에서 JDBC까지
일반적인 호출 흐름은 다음과 같습니다:
SqlSession -> Executor -> StatementHandler -> JDBC Statement
-> ResultSetHandler
SqlSession은 파사드이며, Executor는 SQL 실행, 트랜잭션 및 캐시를 담당합니다. MappedStatement은 매핑된 SQL을 나타내고, StatementHandler는 파라미터 설정과 실행을 담당하며, ResultSetHandler는 결과 객체 생성을 담당합니다.
2. 동적 SQL과 파라미터
XML의 동적 노드는 최종적으로 SqlSource로 조합됩니다. #{}는 파라미터 매핑을 생성하고 PreparedStatement를 사용합니다. ${}는 문자열 치환 방식이므로, 화이트리스트로 관리되는 테이블명, 컬럼명 및 정렬 필드에만 사용해야 합니다.
OGNL은 일부 동적 표현식을 평가하지만, 표현식 기능이 곧 보안 경계는 아닙니다. 신뢰할 수 없는 입력이 표현식이나 SQL 구조를 제어하게 두지 마세요.
3. Executor
SimpleExecutor는 매번 Statement를 생성하고, ReuseExecutor는 Statement를 재사용합니다. BatchExecutor는 일괄 업데이트를 실행 시점까지 미루며, CachingExecutor는 다른 Executor를 데코레이트해 네임스페이스 수준의 2차 캐시를 제공합니다.
Executor를 선택할 때는 트랜잭션, 실패 후 재시도, 메모리 사용량 및 JDBC 드라이버를 함께 고려해야 합니다. BatchExecutor를 단순히 활성화한다고 반드시 더 빨라지는 것은 아닙니다.
4. 1차 캐시와 2차 캐시
1차 캐시는 일반적으로 SqlSession/Executor의 수명 범위 안에 있으므로, 장시간 재사용하면 오래된 데이터를 읽을 수 있습니다. 2차 캐시는 네임스페이스 단위에 있으며, 직렬화, 트랜잭션 커밋 및 무효화 시점의 일관성과 관련됩니다. 인스턴스 간 강한 일관성이 필요한 환경에서는 2차 캐시를 기본으로 활성화해서는 안 됩니다.
5. 소스 코드 읽는 방법
먼저 Mapper 프록이를 따라가고, 그다음 MappedStatement 등록 과정, Executor 데코레이터 체인, ParameterMapping, TypeHandler 및 ResultMap을 따라가세요. 업그레이드할 때는 실제로 고정한 버전의 소스 코드를 기준으로 삼아야 하며, 내부 필드를 안정적인 API로 간주하지 마세요.