ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 스프링 DB 데이터접근 기술 - MyBatis
    Spring-Boot/스프링 DB 2편 - 데이터 접근 기술 2022. 9. 17. 17:21

    - JdbcTemplate과 비교해서 MyBatis의 가장 매력적인 점은 SQL을 XML에 편리하게 작성할 수 있고 또 동적 쿼리를 매우 편리하게 작성할 수 있다.

    - MyBatis는 XML에 작성하기 떄문에 라인이 길어져도 문자 더하기에 대한 불편함이 없다.

    - JdbcTemplate은 자바 코드로 직접 동적쿼리를 작성해야한다. 반면에 MyBatis는 동적 쿼리를 매우 편리하게 작성할 수 있는 다양한 기능들을 제공해준다.

     

    * 설정의 장단점 *

    JdbcTemplate은 스프링에 내장된 기능이고, 별도의 설정없이 사용할 수 있다는 장점있다. 반면에 MyBatis는 약간의 설정이 필요하다

     

    * 정리 *

    - 프로젝트에서 동적 쿼리는 복잡한 쿼리가 많다면 MyBatis를 사용하고, 단순한 쿼리들이 많으면 JdbcTemplate을 선택해서 사용하면 된다. 물론 둘을 함께 사용해도 된다. 하지만 MyBatis를 선택했다면 그것으로 충분할 것이다


    MyBatis 설정

    ```properties
    
    #MyBatis
    mybatis.type-aliases-package=hello.itemservice.domain
    mybatis.configuration.map-underscore-to-camel-case=true
    logging.level.hello.itemservice.repository.mybatis=trace

    - `mybatis.type-aliases-package`

        ㆍ마이바티스에서 타입정보를 사용할 때는 패키지 이름을 적어줘야하는데,

             여기에 명시하면 패키지 이름을 생략할 수 있다

        ㆍ지정한 패키지와 그 하위 패키지가 자동으로 인식된다

        ㆍ여러 위치를 지정하려면 `,` `;` 로 구분하면 된다

    - `mybatis.configuraiton.map-underscore-to-camel-case`

        ㆍJdbcTemplate의 `BeanPropertyRowMapper` 에서 처럼 언더바를 카멜로 자동 변경해주는 기능을 활성화한다

    - `logging.level.hello.itemservice.repository.mybatis=trace`

        ㆍMyBatis에서 실행되는 쿼리 로그를 확인할 수 있다.

     

    * 관례의 불일치 *

    - 자바 객체에는 주로 camelCase 표기법을 사용

    - 반면에 관계형 데이터베이스에서는 주로 언더스코어를 사용하는 snake_case 표기법 사용

    - 이렇게 관례로 많이 사용하다 보니 `map-underscore-to-camel-case` 기능을 활성화하면 언더스코어 표기법을 카멜로 자동으로 변환해준다. 

    - 컬럼 이름과 객체 이름이 완전히 다른 경우에는 조회 SQL에서 별칭을 사용하면 된다

        ㆍDB `select item_name` / 객체 `name`

        ㆍ별칭을 통한 해결방안 - `select item_name as name`


    MyBatis 적용1 - 기본

    - 마이바티스 매핑 XML을 호출해주는 매퍼 인터페이스만들기

    - 이 인터페이스는 `@Mapper` 애노테이션을 붙여주어야 한다. 그래야 MyBatis에서 인식할 수 있다

    - 이 인터페이스의 메서드를 호출하면 다음에 보이는 `xml`의 해당 SQL을 실행하고 그 결과를 돌려준다

     

    - namespace : 앞서 만든 매퍼 인터페이스 경로 지정

     

    * 참고 - XML 파일 경로 수정하기 *

    - XML파일을 원하는 위치에 두고 싶으면 `application.properties` 설정

    mybatis.mapper-locations=classpath:mapper/**/*.xml

    - resources/mapper 를포함한 그 하위 폴더에 있는 XML을 XML 매핑 파일로 인식

     

    - 파라미터는 #{} 문법을 사용하면된다. 그리고 매퍼에서 넘긴 객체의 프로퍼티 이름을 적어준다

    - #{} 문법을 사용하면 PreparedStatement 를 사용한다. JDBC의 `?`를 치환한다고 생각

    - `useGeneratedKeys` 는 데이터베이스가 키를 생성해주는 `IDENTITY` 전략일 떄 사용한다.

    - `keyProperty` 는 생성되는 키의 속성 이름을 지정한다.

    - 파라미터가 1개만 있으면 `@Param` 을 지정하지 않아도 되지만, 파라미터가 2개 이상이면 `@Param` 으로 이름을 지정해서 파라미터를 구분해야 한다.

    - `resultType`은 반환 타입을 명시하면 된다. 

        ㆍ앞서 `applicaiton.properties`에 'mybatis.type-aliases-package=hello.itemservice.domain' 속성을 지정한 덕분에

           패키지 명을 다 적지는 않아도 된다. 그렇지 않으면 모든 패키지 명을 다 적어야 한다.

    - `<where>`  은 적절하게 `where` 문장을 만들어준다.

     

    * XML 특수 문자 *

    - XML에서는 데이터 영역에 `<`, `>` 같은 특수 문자를 사용할 수 없다.

    < : &lt;
    > : &gt;
    & : &amp;

    - 다른 해결 방안으로는 XML에서 지원하는 `CDATA' 구문 문법을 사용하는 것이다

    <if test="maxPrice != null">
        <![CDATA[
        and price <= #{maxPrice}
        ]]>
    </if>

    Mybatis 적용3 - 분석

    1. 애플리케이션 로딩 시점에 MyBatis 스프링 연동 모듈은 `@Mapper`가 붙어있는 인터페이스를 조사한다

    2. 해당 인터페이스가 발견되면 동적 프록시 기술을 사용해서 `ItemMapper` 인터페이스의 구현체를 만든다

    3. 생성된 구현체를 스프링 빈으로 등록한다.

     

    * 매퍼 구현체 *

    - 마이바티스 스프링 연동 모듈이 만들어주는 `ItemMapper`의 구현체 덕분에 인터페이스만으로 편리하게

     XML의 데이터를 찾아서 호출할 수 있다

    - 원래 마이바티스를 사용하려면 더 번잡한 코드를 거쳐야 하는데, 이런 부분을 인터페이스 하나로 매우 깔끔하고 편리하게 사용할 수 있다

    - 매퍼 구현체는 예외 변환까지 처리해준다. MyBatis에서 발생한 예외를 스프링 예외 추상화인 `DataAccessException`에 맞게 변환해서 반환해준다. JdbcTemplate이 제공하는 예외 변환 기능을 여기서도 제공한다고 이해하면 된다

     

    * 정리 *

    - 매퍼 구현체 덕분에 마이바티스를 스프링에 편리하게 통합해서 사용할 수 있다

    - 매퍼 구현체를 사용하면 스프링 에외 추상화도 함께 적용된다

    - 마이바티스 스프링 연동 모듈이 많은 부분을 자동으로 설정해주는데, 데이터베이스 커넥션, 트랜잭션과 관련된 기능도 마이바티스와 함께 연동하고, 동기화해준다.


    Mybatis 기능 정리1 - 동적 쿼리

    동적 SQL

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

    - 기능

        ㆍif

        ㆍchoose (when, otherwise)

        ㆍtrim (where, set)

        ㆍforeach

     

     

    * if *

    - 해당 조건에 따라 값을 추가할지 말지 판단

     

    * choose, when, otherwise *

    - 자바의 switch 구문과 유사한 구문도 사용 가능

     

    * trim, where, set *

    * <where> 사용 *

    - `<where>` 는 문장이 없으면 `where`를 추가하지 않는다.

    - 문장이 있으면 `where`를 추가한다.

    - 만약 `and`가 먼저 시작된다면 `and`를 지운다.

     

    - trim으로 기능 대체 가능 

     

    * foreach *

    - 컬렉션을 반복처리할 때 사용한다.

    - 파라미터로 List를 전달하면 된다.


    Mybatis 기능 정리2 - 기타기능

    애노테이션으로 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이 해결되지 않으므로 간단한 경우에만 사용한다.

     

    문자열 대체 ( String Substitution )

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

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

    - ex : `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>` 조각을 찾아서 사용할 수 있다.

    - 프로퍼티 값을 전달할 수 있고, 해당 값은 내부에서 사용할 수 있다.

     

    Result Maps

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

    - 별칭을 사용하지 않고도 문제 해결 가능

    <resultMap id="userResultMap" type="User">
     <id property="id" column="user_id" />
     <result property="username" column="username"/>
     <result property="password" column="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에서는 들어가는 공수도 많고, 성능을 최적화도 어렵다. 따라서 해당기능 사용 시 신중할 것

    댓글

Designed by Tistory.