[멋쟁이사자처럼부트캠프] 진짜 좋은거 같은 MyBatis
오늘字
아무리 생각해봐도 MyBatis가 최고인 것 같다. 하지만 어째서 Orcle은 JDBC을 더 개선하지 않을까? 그래서 오늘 배운 자원관리 개념과 XML 문법, 동적 SQL을 정리해보겠습니다.
Human Error
본격적인 내용에 앞서, 기억해야될 실수 하나...언제나 컴퓨터는 죄가 없습니다..
Trouble
List<EmpDTO> list6 = session.selectList("com.config.EmpMapper.selectif2", dto2);
EmpDTO dto2 = new EmpDTO();
dto2.setJob("SALESMAN");
dto2.setSal(800);프로그래밍의 [순차적 실행(Sequential Execution)] 원칙으로 인해 먼저 호출된 쿼리(무슨 생각을 하고 있었길래..)가 값 없이 실행되었습니다. 반드시 변수를 먼저 선언하고 설정한 뒤에 쿼리를 호출해야 합니다ㅠㅠ
EmpDTO dto2 = new EmpDTO();
// 쿼리 실행 '전'에 조건을 먼저 설정
dto2.setJob("SALESMAN");
dto2.setSal(800);
// 설정된 조건이 포함된 DTO를 전달하며 쿼리 호출
List<EmpDTO> list6 = session.selectList("com.config.EmpMapper.selectif2", dto2);자원관리
JDBC를 사용할 때는 각 메소드마다 커넥션을 만들고 close() 하는 방식이 단순히 커넥션을 초기화하는 과정으로만 생각했습니다. 하지만 아래 사진처럼 "생성→사용→반납" 패턴입니다. 정말 비효율적으로 보이기에,

MyBatis에서는 성능 이슈를 해결하기 위해 커넥션 풀(Connection Pool) 방식을 도입했습니다.

- 커넥션 풀이란? 네트워크 경로 탐색 등 커넥션 생성에 드는 큰 비용을 줄이기 위해, 커넥션을 미리 여러 개 만들어두고 필요할 때 빌려주고 사용 후 반환받는 방식입니다. 마치 VPN처럼 전용 통로를 열어두고 운용하는 방식과 비슷합니다.
- close()는 필수! 풀(Pool) 환경에서
close()는 실제 연결을 끊는 것이 아니라 풀에 커넥션을 반납하는 것을 의미합니다. 통로를 닫지 않고 놔두면 자원이 계속 고갈되므로, 누락 시 다른 요청이 커넥션을 받지 못해 장애가 발생할 수 있습니다. 따라서 세션의close()는 필수입니다.
XML 문법
MyBatis Mapper의 XML 파일에서는 select, insert, update, delete 태그를 사용하며, 구문을 식별하기 위한 id 속성이 필수입니다.
| 속성 | 설명 |
|---|---|
| id | 구문을 찾기 위해 사용될 수 있는 네임스페이스 내 유일한 구분자 |
| parameterType | 구문에 전달될 파라미터의 패키지 경로를 포함한 전체 클래스명이나 별칭 |
| resultType | 구문에 의해 리턴되는 기대 타입의 패키지 경로를 포함한 전체 클래스명이나 별칭. collection인 경우 collection 타입 자체가 아닌 collection 이 포함된 타입이 될 수 있다. resultType이나 resultMap을 사용하라. |
바인딩 변수
기존 JDBC에서 파라미터를 넘길 때 사용하던 ? 기호는 MyBatis에서 #{}로 바뀝니다. #{} 문법을 사용하면 MyBatis가 내부적으로 PreparedStatement 파라미터를 생성하여 안전하게 값을 세팅해 줍니다.
${} 문자열 치환도 있지만, 사용을 지양하고 #{}를 사용하는 것이 안전합니다.
CRUD
| 작업 | 호출 메소드 | 리턴값 의미 | 트랜잭션 |
|---|---|---|---|
| Insert | session.insert(id, param) |
저장된 행(row) 수입니다. | commit() 필요합니다. |
| Update | session.update(id, param) |
수정된 행(row) 수입니다. | commit() 필요합니다. |
| Delete | session.delete(id, param) |
삭제된 행(row) 수입니다. | commit() 필요합니다. |
MyBatis에서 DML(데이터 조작어)인 Insert, Update, Delete를 실행하면 리턴되는 값은 수정된 행(row)의 수(int)입니다.
- 자동 커밋 비활성화: 기존 JDBC와 달리 MyBatis는 기본적으로 오토커밋(Auto-commit)이 비활성화되어 있습니다.
- 트랜잭션 처리: 따라서
session.insert(),session.update(),session.delete()를 호출하여 성공 메시지를 확인했더라도, 반드시session.commit()을 호출해야 실제 DB에 반영됩니다. 실패 시에는session.rollback()으로 트랜잭션을 되돌려야 합니다.
비교 연산자
Mapper SQL 내부에서 < 나 > 같은 비교 연산자를 그대로 사용하면, XML 파서가 이를 XML 태그의 시작/종료로 오인하여 에러가 발생합니다.
이를 회피하는 방법은 두 가지입니다:
- 엔티티 치환:
<는<,>는>로 바꾸어 씁니다.
<select id="id" resultType="DTO" parameterType="type" >
select *
from tableName
where columnName > #{columnName}
</select>- CDATA 활용: 쿼리 전체 또는 해당 부분을
<![CDATA[ ... ]]>로 감싸서, 파서가 해석하지 않고 순수한 문자 데이터로 처리하도록 만듭니다. (실무에서 널리 쓰이는 방식입니다.)
<select id="id" resultType="DTO" parameterType="type" >
<![CDATA[select * from tableName where columnName < #{columnName}]]>
</select>동적 SQL (Dynamic SQL)
foreach
<foreach> 태그는 List나 배열 같은 컬렉션을 반복하여 SQL 조각을 생성합니다.
- 멀티 Insert:
VALUES뒤의( ... )튜플을 반복 생성하며separator=","로 쉼표를 자동 삽입합니다.
<insert id="insertMulti" parameterType="arraylist">
INSERT INTO EMP (EMPNO, ENAME, SAL)
VALUES
<foreach collection="list" item="dto" separator=",">
(#{dto.empno}, #{dto.ename}, #{dto.sal})
</foreach>
</insert>- 멀티 Delete / Select(IN):
IN조건 절에 값을 나열할 때 사용합니다.open="(",close=")"속성을 활용하면 반복 전후에 괄호를 자동으로 생성해주어 문법 오류를 방지할 수 있습니다.
<!-- 멀티 delete-->
<delete id="deleteMulti" parameterType="arraylist">
delete from EMP
<where>
<foreach item="num" separator="," collection="list" open="empno IN (" close=")">
#{num}
</foreach>
</where>
</delete>
<!-- 멀티 select-->
<select id="selectMulti" parameterType="arraylist" resultType="EmpDTO">
select empno, ename, sal from EMP
<where>
<foreach collection="list" item="ename" separator="," open="ename IN(" close=")">
#{ename}
</foreach>
</where>
</select>if 와 where
- 가장 빈번하게 쓰이는
<if>태그는 조건에 따라 SQL 조각을 덧붙입니다. 여러<if>조건을 조합할 때<where>태그로 감싸주면 아주 유용합니다. 조건이 하나도 만족하지 않으면WHERE절을 아예 만들지 않고, 조건이 생성될 때 문장 시작에 불필요한AND나OR가 있다면 알아서 제거해 줍니다.
<select id="findActiveBlogWithTitleLike"
resultType="Blog">
SELECT * FROM BLOG
WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
</if>
</select>set으로 조건부 Update
- 동적 Update 구문에서 수정할 컬럼이 조건에 따라 달라질 때
<set>태그를 사용합니다.<set>태그 안에서<if>를 통해 컬럼을 업데이트하도록 구성하면, 마지막에 남는 불필요한 쉼표(,)를 자동으로 제거해주어 SQL 문법 오류를 완벽히 막아줍니다.
<select id="updateif" parameterType="EmpDTO" resultType="EmpDTO">
update EMP
<set>
<if test="empno != 0">
empno=#{empno},
</if>
<if test="ename != null">
ename = #{ename},
</if>
<if test="sal != 0">
sal = #{sal}
</if>
</set>
where empno=#{empno}
</select>choose, when, otherwise
- 자바의
switch-case문과 유사한 분기 처리 구조입니다. 여러 조건 중 최초로 참(true)이 되는<when>블록 하나만 택하며, 어떤 조건도 맞지 않으면<otherwise>블록의 구문을 적용합니다.
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>출처


