728x90
예시 시나리오
사용자가 아래와 같은 조건을 넘겨준다고 가정해요:
- 검색어: name = "홍길동"
- 정렬 기준: orderBy = "created_at"
- 정렬 방향: orderDir = "desc"
- 페이지 번호: page = 2, 페이지 크기: size = 10
1. 검색 + 정렬 + 페이징 DTO
public class UserSearchCondition {
private String name;
private String orderBy; // 정렬할 컬럼명
private String orderDir; // ASC / DESC
private int page; // 현재 페이지
private int size; // 페이지당 개수
}
2. Mapper 인터페이스
List<User> searchUsers(UserSearchCondition cond);
3. Mapper XML: 동적 정렬 + 페이징 처리
<select id="searchUsers" resultType="User" parameterType="UserSearchCondition">
SELECT * FROM users
<where>
<if test="name != null and name != ''">
AND name LIKE CONCAT('%', #{name}, '%')
</if>
</where>
<if test="orderBy != null and orderBy != ''">
ORDER BY ${orderBy}
<if test="orderDir != null and orderDir != ''">
${orderDir}
</if>
</if>
LIMIT #{size} OFFSET #{page} * #{size}
</select>
주의사항: ${} vs #{}
표현 | 설명 | 주의사항 |
${} | 문자열 치환 | SQL 인젝션 위험 있음, 반드시 사용자 입력 검증 필요 |
#{} | PreparedStatement 방식 바인딩 | 안전하고 권장됨 |
${orderBy}, ${orderDir}는 반드시 화이트리스트 검증을 통해만 사용해야 합니다.
예: orderBy는 "name", "created_at", "id" 중에만 허용
안전하게 정렬 컬럼 제한하는 방법
1. Enum 클래스 또는 유효성 검사 코드
private static final Set<String> ALLOWED_COLUMNS = Set.of("name", "created_at", "id");
2. Mapper 호출 전에 검증
if (!ALLOWED_COLUMNS.contains(cond.getOrderBy())) {
cond.setOrderBy("id"); // 기본 정렬 컬럼
}
페이징 계산 팁
int page = cond.getPage(); // 1부터 시작
int size = cond.getSize();
int offset = (page - 1) * size;
그리고 XML에서는 이렇게:
LIMIT #{size} OFFSET #{offset}
마무리 정리
항목 | 기능사용 | 예 |
동적 정렬 | ORDER BY ${orderBy} ${orderDir} | 컬럼/방향 조건부 정렬 |
페이징 | LIMIT #{size} OFFSET #{offset} | 페이지 기반 조회 |
보안 | ${}는 사용자 입력 검증 필수 | SQL 인젝션 주의 |
응용 예
- 게시판 목록 + 정렬 + 페이징
- 관리자 목록 검색 + 등록일 내림차순 정렬
- 상품 검색 + 가격순 정렬 + 페이지 이동
LIST
'Mybatis > 동적 SQL 작성' 카테고리의 다른 글
동적 조건 검색 처리 (0) | 2025.04.17 |
---|---|
if, choose, where, trim 태그 사용 (0) | 2025.04.17 |