ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [김영한 스프링] 12. MyBatis - 기능 정리
    Spring/스프링 DB 2편 - 데이터 접근 활용 기술 2024. 1. 9. 22:29

    MyBatis 기능 정리1 - 동적 쿼리

     

     

    MyBatis에서 자주 사용하는 주요 기능을 공식 메뉴얼이 제공하는 예제를 통해 간단히 정리해 보자.

    • MyBatis 공식 메뉴얼 : https://mybatis.org/mybatis-3/ko/index.html
    • MyBatis 스프링 공식 메뉴얼 : https://mybatis.org/spring/ko/index.html

     

     

    동적 SQL

    마이바티스가 제공하는 최고의 기능이자 마이바티스를 사용하는 이유는 바로 동적 SQL 기능 때문이다.

    동적 쿼리를 위해 제공되는 기능은 다음과 같다

    • if
    • choose (when, otherwise)
    • trim (where, set)
    • foreach

     

    공식 매뉴얼에서 제공하는 예제를 통해 동적 SQL을 알아보자.

     

     

    if

    <select id="findActiveBlogWithTitleLike" resultType="Blog">
        SELECT * FROM BLOG
         WHERE state = ‘ACTIVE’
          <if test="title != null">
           AND title like #{title}
          </if>
    </select>
    • 해당 조건에 따라 값을 추가할지 말지 판단한다.
    • 내부의 문법은 OGNL을 사용한다. 자세한 내용은 OGNL을 검색해 보자.

     

     

    choose, 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>
    • 자바의 switch 구문과 유사한 구문도 사용할 수 있다.

     

     

    trim, where, set

    <select id="findActiveBlogLike" resultType="Blog">
        SELECT * FROM BLOG
         WHERE
          <if test="state != null">
               state = #{state}
          </if>
          <if test="title != null">
           AND title like #{title}
          </if>
          <if test="author != null and author.name != null">
           AND author_name like #{author.name}
          </if>
    </select>

     

     

    이 예제의 문제점은 문장을 모두 만족하지 않을 때 발생한다.

    SELECT * FROM BLOG
    WHERE

     

     

    title 만 만족할 때도 문제가 발생한다.

    SELECT * FROM BLOG
    WHERE
    AND title like ‘someTitle’

     

    결국 WHERE 문을 언제 넣어야 할지 상황에 따라서 동적으로 달라지는 문제가 있다.

    <where>를 사용하면 이런 문제를 해결할 수 있다.

     

     

    <where> 사용

    <select id="findActiveBlogLike" resultType="Blog">
        SELECT * FROM BLOG
        <where>
         <if test="state != null">
               state = #{state}
         </if>
         <if test="title != null">
           AND title like #{title}
         </if>
         <if test="author != null and author.name != null">
           AND author_name like #{author.name}
         </if>
        </where>
    </select>

    <where>는 문장이 없으면 where를 추가하지 않는다. 문장이 있으면 where를 추가한다. 만약 and가 먼저 시작된다면 and를 지운다.

     

    참고로 다음과 같이 trim이라는 기능으로 사용해도 된다. 이렇게 정의하면 <where>와 같은 기능을 수행한다.

    <trim prefix="WHERE" prefixOverrides="AND |OR ">
     ...
    </trim>

     

     

    foreach

    <select id="selectPostIn" resultType="domain.blog.Post">
        SELECT * FROM POST P
        <where>
         <foreach item="item" index="index" collection="list"
          open="ID in (" separator="," close=")" nullable="true">
           #{item}
         </foreach>
        </where>
    </select>
    • 컬렉션을 반복 처리할 때 사용한다. where in (1,2,3,4,5,6) 와 같은 문장을 쉽게 완성할 수 있다.
    • 파라미터로 List를 전달하면 된다.

     

    참고
    동적 쿼리에 대한 자세한 내용은 다음을 참고하자.
    https://mybatis.org/mybatis-3/ko/dynamic-sql.html

     

     

    MyBatis 기능 정리2 - 기타 기능

     

     

    애노테이션으로 SQL 작성

    다음과 같이 XML 대신에 애노테이션에 SQL을 작성할 수 있다.

    @Select("select id, item_name, price, quantity from item where id=#{id}")
    Optional<Item> findById(Long id);
    • @Insert, @Update, @Delete, @Select 기능이 제공된다.
    • 이 경우 XML에는 <select id="findById"> ~ </select>는 제거해야 한다.
    • 동적 SQL이 해결되지 않으므로 간단한 경우에만 사용한다.

     

    애노테이션으로 SQL 작성에 대한 더 자세한 내용은 다음을 참고하자.

    • https://mybatis.org/mybatis-3/ko/java-api.html

     

     

    문자열 대체(String Substitution)

    #{} 문법은 ?를 넣고 파라미터를 바인딩하는 PreparedStatement를 사용한다.

    때로는 파라미터 바인딩이 아니라 문자 그대로를 처리하고 싶은 경우도 있다. 이때는 ${}를 사용하면 된다.

     

    다음 예제를 보자

    ORDER BY ${columnName}

     

    @Select("select * from user where ${column} = #{value}")
    User findByColumn(@Param("column") String column, @Param("value") String value);

     

    주의

    ${}를 사용하면 SQL 인젝션 공격을 당할 수 있다. 따라서 가급적 사용하면 안 된다. 사용하더라도 매우 주의 깊게 사용해야 한다.

     

     

    재사용 가능한 SQL 조각

    <sql>을 사용하면 SQL 코드를 재사용할 수 있다.

    <sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql>

     

    <select id="selectUsers" resultType="map">
        select
        <include refid="userColumns"><property name="alias" value="t1"/></include>,
        <include refid="userColumns"><property name="alias" value="t2"/></include>
        from some_table t1
        cross join some_table t2
    </select>
    • <include>를 통해서 <sql> 조각을 찾아서 사용할 수 있다.

     

    <sql id="sometable">
        ${prefix}Table
    </sql>
    
    <sql id="someinclude">
        from
          <include refid="${include_target}"/>
    </sql>
    
    <select id="select" resultType="map">
        select
          field1, field2, field3
        <include refid="someinclude">
          <property name="prefix" value="Some"/>
          <property name="include_target" value="sometable"/>
        </include>
    </select>
    • 프로퍼티 값을 전달할 수 있고, 해당 값은 내부에서 사용할 수 있다.

     

     

    Result Maps

    결과를 매핑할 때 테이블은 user_id이지만 객체는 id이다.

    이 경우 컬럼명과 객체의 프로퍼티 명이 다르다. 그러면 다음과 같이 별칭(as)을 사용하면 된다.

    <select id="selectUsers" resultType="User">
        select
          user_id as "id",
          user_name as "userName",
          hashed_password as "hashedPassword"
        from some_table
        where id = #{id}
    </select>

     

    별칭을 사용하지 않고도 문제를 해결할 수 있는데, 다음과 같이 resultMap을 선언해서 사용하면 된다.

    <resultMap id="userResultMap" type="User">
        <id property="id" column="user_id" />
        <result property="username" column="user_name"/>
        <result property="password" column="hashed_password"/>
    </resultMap>
    
    <select id="selectUsers" resultMap="userResultMap">
        select user_id, user_name, hashed_password
        from some_table
        where id = #{id}
    </select>

     

     

    복잡한 결과매핑

    MyBatis도 매우 복잡한 결과에 객체 연관관계를 고려해서 데이터를 조회하는 것이 가능하다.

    이때는 <association>, <collection> 등을 사용한다.

    이 부분은 성능과 실효성에서 측면에서 많은 고민이 필요하다.

    JPA는 객체와 관계형 데이터베이스를 ORM 개념으로 매핑하기 때문에 이런 부분이 자연스럽지만, MyBatis에서는 들어가는 공수도 많고, 성능을 최적화하기도 어렵다. 따라서 해당기능을 사용할 때는 신중하게 사용해야 한다.

    해당 기능에 대한 자세한 내용은 공식 메뉴얼을 참고하자.

     

    참고
    결과 매핑에 대한 자세한 내용은 다음을 참고하자.
    https://mybatis.org/mybatis-3/ko/sqlmap-xml.html#Result_Maps

     

     

    출처 :  https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-db-2/dashboard

     

    스프링 DB 2편 - 데이터 접근 활용 기술 강의 - 인프런

    백엔드 개발에 필요한 DB 데이터 접근 기술을 활용하고, 완성할 수 있습니다. 스프링 DB 접근 기술의 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니다., 백엔드 개발자

    www.inflearn.com

Designed by Tistory.