TIL 3/27 - MyBatis(UPDATE, DELETE, SELECT)
TypeAlias
https://mybatis.org/mybatis-3/ko/configuration.html#typeAliases
public int updateMenu(SqlSession sqlSession, MenuDto menu) {
return sqlSession.update("Menu.update", menu);
}
public int deleteMenu(SqlSession sqlSession, int code) {
return sqlSession.delete("Menu.delete", code);
} // DAO의 sql 로직
<update id="update" parameterType="Menu">
UPDATE
tbl_menu
SET
menu_name = #{menuName}
, menu_price = #{menuPrice}
, category_code = #{categoryCode}
, orderable_status = #{orderableStatus}
WHERE
menu_code = #{menuCode}
</update>
<delete id="delete">
DELETE
FROM
tbl_menu
WHERE
menu_code = #{code}
</delete> // mapper.xml 파일
SELECT
조회 결과가 매번 다르기에, 반환 타입에 신경써주어야함.
<select id="selectList" resultType="string">
<!-- resultType || resultMap이 올 수 있음. -->
SELECT
menu_name
FROM
tbl_menu
</select>
DTO를 통한 매핑
<select id="selectMenuList" resultType="Menu">
SELECT
menu_code AS menuCode
, menu_name AS menuName
, menu_price AS menuPrice
, category_code AS categoryCode
, orderable_status AS orderableStatus
FROM
tbl_menu
WHERE
orderable_status = 'y'
</select>
그러나 칼럼명을 자동으로 매핑해주지는 않기 때문에 각각에 대해 Alias 처리를 해주어야 제대로 모델에 삽입된다.
int형으로 해서 null값에 대해 자동으로 0이 매핑된다.
boxed객체인 Integer로 DTO의 필드를 설정하게 되면 제대로 동작한다.
또, Alias에 오탈자가 나와 매핑이 제대로 되지 않을 경우 해당 값에 대해서는 null 혹은 0 값이 삽입된다.
이렇듯 매번 Alias를 통한 매핑이 필수적이라, ResultMap을 통한
resultMap을 통한 매핑
특정 컬럼값을 특정 필드에 매핑시키는 resultMap을 한번 정의해두고 재사용하기, 주로 mapper 파일에 작성
<resultMap id="identifier" type="DTOType">
<id|result column="columnName" property="DTOfieldName"/>
</resultMap>
id → PK, 내부적으로 성능 향상의 효과가 있음
result → 일반키
<resultMap id="menuMap" type="Menu">
<id column="menu_Code" property="menuCode"/>
<result column="menu_name" property="menuName"/>
<result column="menu_price" property="menuPrice"/>
<result column="category_code" property="categoryCode"/>
<result column="orderable_status" property="orderableStatus"/>
</resultMap> <!-- resultMap의 예시 -->
<select id="selectMenuList" resultMap="menuMap">
SELECT
menu_code
, menu_name
, menu_price
, category_code
, orderable_status
FROM
tbl_menu
WHERE
orderable_status = 'y'
</select> <!-- resultMap을 통해 결과를 받아올 경우,
resultType 대신 resultMap으로 변경하고,
resultMap의 이름으로 변경해주면 된다 :) -->
일부 컬럼만 조회할 경우에도 정상 작동하는걸 볼 수 있다.
훨씬 효율적인 방법일듯하다.
어지간해선 오류가 나지 않지만, property명 오탈자가 날 경우 예외발생한다.
SelectOne 을 통한 조회시, 꼭 null 처리를 해주어야 한다 ..
그리고 꼭 해당 조회값이 있는지 확인해보자. 삽질하기 싫으면 ,,
<select id="selectMenuByCode" parameterType="_int" resultMap="menuMap">
SELECT
menu_code
, menu_name
, menu_price
, category_code
, orderable_status
FROM
tbl_menu
WHERE
AND menu_code = #{code}
</select>
SelectOne을 통한 조회시의 mapper 쿼리 -> 타입명 일치 꼭 꼭 시켜주자. resultMap, resultType 등 확인도 꼭..
but 위와 같은 방식으로 할 경우, 개발자의 단순 반복으로 값 누락이나 문자열리터럴 오탈자가 발생할 수 있음.
→ DAO 클래스 대신 Mapper 인터페이스를 둔다.
service에서 SqlSession을 통해 해당 Mapper 인터페이스의 메소드를 호출만 함
MyBatis가 자동으로 Mapper 인터페이스에 해당하는 쿼리를 바인딩해서 실행
해당 xml과 매핑되는 Mapper 인터페이스
xml의 namespace에 매핑시키고자하는 Mapper 인터페이스의 풀네임 작성
id == interface의 메소드명, parameterType == 추상메소드의 파라미터타입
DML문 → int
SELECT(단일행) == resultType, SELECT(다중행) == List<resultType>
<insert id="insertMenu" parameterType="Menu">
INSERT INTO
tbl_menu
(
menu_name
, menu_price
, orderable_status
)
VALUES
(
#{menuName}
, #{menuPrice}
, #{orderableStatus}
)
</insert> // 추상 메소드 작성을 위한 mapper xml
int insertMenu(MenuDto menu);
의 형태로 작성해주면 된다.
그리고 이전의 DAO를 통해 쿼리를 날리던 서비스 단의 코드를 손봐주자
public class MenuService {
public int insertMenu(MenuDto menu){
SqlSession sqlSession = getSqlSession();
MenuMapper menuMapper = sqlSession.getMapper(MenuMapper.class);
int result = menuMapper.insertMenu(menu);
if(result > 0) {
sqlSession.commit();
}else{
sqlSession.rollback();
}
sqlSession.close();
return result;
}
public List<MenuDto> selectMenu() {
SqlSession sqlSession = getSqlSession();
MenuMapper menuMapper = sqlSession.getMapper(MenuMapper.class);
List<MenuDto> list = menuMapper.selectMenu();
sqlSession.close();
return list;
}
public MenuDto selectMenuByCode(int code) {
SqlSession sqlSession = getSqlSession();
MenuMapper menuMapper = sqlSession.getMapper(MenuMapper.class);
MenuDto menu = menuMapper.selectMenuByCode(code);
sqlSession.close();
return menu;
}
}
위를 보면 알 수 있듯이, sqlSession객체를 만들고 이전처럼 DAO단에 넘겨주는게 아니라,
직접 getMapper를 통해 MenuMapper 객체를 생성하고, 내부의 추상 메소드를 사용하여 DB와 쿼리를 통해 통신한다 :)
즉, 쿼리 짜고 → MenuMapper 인터페이스에 추상 메소드 생성 → 서비스단에서 Mapper 불러오기 및 쿼리날리기 → 쿼리 닫고 서비스에서 원하는 자료형 리턴 → 컨트롤러에서 데이터 받아 사용
≥ 연산자를 쓸 경우, 컴파일에러가 발생하는데, 이의 방지를 위해 c데이터 형태로 감싸주자.
<select id="selectUnderCostCount" parameterType="_int" resultType="_int">
<![CDATA[
SELECT
COUNT(*)
FROM
tbl_menu
WHERE
menu_price <= #{menuPrice}
]]>
</select>
SELECT
COUNT(*)
FROM
tbl_menu
WHERE
menu_price<![CDATA[<=]]>#{menuPrice}
요런 식으로 해주자.
근데 쿼리 조회시 컬럼의 snake_case → cameCase변환 세팅을 할 수 있다.
<settings>
<setting name="mapUnderscoreToCamelCase" value="true" />
</settings>
mybatis-config.xml 내부에서 해당 세팅을 해주게 될 경우, 자동으로 snake_case를 camelCase로 변경해주게 된다.
Mapper Interface화의 장점
SQL문과 Java 메소드가 명확히 매핑 ( 직관적 연결 → 쉽게 찾기 가능 → 유지보수 용이 )
코드 간결성
SqlSession객체 주입 불필요 (MyBatis에서 자동으로 쿼리문 찾아 실행)