Docs Java Spring

mybatis

mybatis

Dynamic SQL

choose(when, otherwise)

foreach

<where>
  <choose>
    <when test="ids == null">
      <if test="name != null">
        displayNm LIKE CONCAT('%', #{name}, '%')
      </if>
    </when>
    <otherwise>
      AND id IN ( <foreach collection="ids" item="id" separator=",">#{id}</foreach> )
    </otherwise>
  </choose>
</where>

insert duplicate update with foreach

on duplicate key update column에 values를 사용

insert into user(name, gender, email)
values
<foreach collection="members" item="item" separator=",">
(
    #{item.name}
  , #{item.gender}
  , #{item.email}
)
</foreach>
on duplicate key update
  name = values(name)
  gender = values(gender)
  email = values(email)

useGeneratedKeys: 자동 생성 키 가져오기

useGeneratedKeys=”true” 로 설정하고 keyProperty에 자동생성키 컬럼을 셋팅

<insert id="insertAuthor" useGeneratedKeys="true" keyProperty="id">
  insert into Author (username,password,email,bio)
  values (#{username},#{password},#{email},#{bio})
</insert>
  • 다중 레코드 입력 시
<insert id="insertAuthor" useGeneratedKeys="true"
    keyProperty="id">
  insert into Author (username, password, email, bio) values
  <foreach item="item" collection="list" separator=",">
    (#{item.username}, #{item.password}, #{item.email}, #{item.bio})
  </foreach>
</insert>

selectKey

  • keyProperty: selectKey구문의 결과가 셋팅될 property(resultType[class]의 멤버변수와 일치)
  • resultType: 결과의 타입
  • order: BEFORE / AFTER
    • BEFORE: selectKey구문의 쿼리를 먼저 실행
    • AFTER: selectKey구문의 쿼리를 마지막에 실행
<insert id="addUser" parameterType="User">
  <selectKey keyProperty="userId,userName" resultType="User" order="AFTER">
    SELECT USER_ID AS userId
         , USER_NAME AS userName
    FROM USER
    ORDER BY USER_ID DESC
    LIMIT 1
  </selectKey>
  INSERT INTO USER(col1, ...)
  VALUES (#{...}, ...)
</insert>
Reference

Mapper XML

Result Maps

  • constructor - 인스턴스화되는 클래스의 생성자에 결과를 삽입하기 위해 사용됨
    • idArg - ID 인자. 전반적으로 성능을 향상
    • arg - 생성자에 삽입되는 일반적인 결과
  • id – ID 결과. 전반적으로 성능을 향상
<id property="id" column="컬럼명"/>
  • result – 필드나 자바빈 프로퍼티에 삽입되는 일반적인 결과
<result property="필드명" column="컬럼명"/>
  • association – 복잡한 타입의 연관관계. 많은 결과는 타입으로 나타난다.
  • 중첩된 결과 매핑 – resultMap 스스로의 연관관계
  • collection – 복잡한 타입의 컬렉션
  • 중첩된 결과 매핑 – resultMap 스스로의 연관관계
  • discriminator – 사용할 resultMap 을 판단하기 위한 결과값을 사용
    • case – 몇가지 값에 기초한 결과 매핑

association

association 엘리먼트는 “has-one”타입의 관계를 다룬다.

마이바티스는 관계를 정의하는 두가지 방법을 제공한다.

  • 내포된(Nested) Select: 복잡한 타입을 리턴하는 다른 매핑된 SQL 구문을 실행하는 방법.
  • 내포된(Nested) Results: 조인된 결과물을 반복적으로 사용하여 내포된 결과 매핑을 사용하는 방법.

연관(Association)을 위한 중첩된 Select

<resultMap id="blogResult" type="Blog">
  <association property="author" column="author_id" javaType="Author" select="selectAuthor"/>
</resultMap>

<select id="selectBlog" resultMap="blogResult">
  SELECT * FROM BLOG WHERE ID = #{id}
</select>

<select id="selectAuthor" resultType="Author">
  SELECT * FROM AUTHOR WHERE ID = #{id}
</select>

위 방법은 “N+1 Selects 문제” 으로 알려진 문제점을 가진다. N+1 조회 문제는 처리과정의 특이성으로 인해 야기된다.

  • 레코드의 목록을 가져오기 위해 하나의 SQL 구문을 실행한다. (“+1” 에 해당).
  • 리턴된 레코드별로 각각의 상세 데이터를 로드하기 위해 select 구문을 실행한다. (“N” 에 해당). 이 문제는 수백 또는 수천의 SQL 구문 실행이라는 결과를 야기할 수 있다. 목록을 로드하고 내포된 데이터에 접근하기 위해 즉시 반복적으로 처리한다면 지연로딩으로 호출하고 게다가 성능은 많이 나빠질 것이다. 그래서 다른 방법이 있다.

관계를 위한 내포된 결과(Nested Results)

개별구문을 실행하는 것 대신에 테이블을 함께 조인

<resultMap id="blogResult" type="Blog">
  <id property="id" column="blog_id" />
  <result property="title" column="blog_title"/>
  <association property="author" javaType="Author">
    <id property="id" column="author_id"/>
    <result property="username" column="author_username"/>
    <result property="password" column="author_password"/>
    <result property="email" column="author_email"/>
    <result property="bio" column="author_bio"/>
  </association>
</resultMap>

<select id="selectBlog" resultMap="blogResult">
  select
    B.id            as blog_id,
    B.title         as blog_title,
    B.author_id     as blog_author_id,
    A.id            as author_id,
    A.username      as author_username,
    A.password      as author_password,
    A.email         as author_email,
    A.bio           as author_bio
  from Blog B left outer join Author A on B.author_id = A.id
  where B.id = #{id}
</select>
  • id 엘리먼트는 내포된 결과 매핑에서 매우 중요한 역할을 담당한다. 결과 중 유일한 것을 찾아내기 위한 한개 이상의 프로퍼티를 명시해야만 한다. 가능하면 결과 중 유일한 것을 찾아낼 수 있는 프로퍼티들을 선택하라. 기본키가 가장 좋은 선택이 될 수 있다.

resultMap 재사용하기

<resultMap id="blogResult" type="Blog">
  <id property="id" column="blog_id" />
  <result property="title" column="blog_title"/>
  <association property="author"
    resultMap="authorResult" />
  <association property="coAuthor"
    resultMap="authorResult"
    columnPrefix="co_" />       <!--결과매핑을 재사용하기 위해 columnPrefix를 명시-->
</resultMap>

<resultMap id="authorResult" type="Author">
  <id property="id" column="author_id"/>
  <result property="username" column="author_username"/>
  <result property="password" column="author_password"/>
  <result property="email" column="author_email"/>
  <result property="bio" column="author_bio"/>
</resultMap>

collection

“has many”타입의 관계를 다룬다.

<resultMap id="blogResult" type="Blog">
  <id property="id" column="blog_id" />
  <result property="title" column="blog_title"/>
  <collection property="posts" ofType="Post">
    <id property="id" column="post_id"/>
    <result property="subject" column="post_subject"/>
    <result property="body" column="post_body"/>
  </collection>
</resultMap>

batch insert

  • insert statement
<insert id="insertName">
insert into table (col1) values (#{value})
</insert>
  • execute a batch in java code
List<String> names = new ArrayList<String>();
names.add("Fred");
names.add("Barney");

SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);

try{
  NameMapper mapper = sqlSession.getMapper(NameMapper.class);
  for(String name : names)
    mapper.insertName(name);
  sqlSession.commit();
}finally{
  sqlSession.close();
}
Reference