Mybatis3: select/insert/update/delete

업데이트:
7 분 소요

비트캠프 서초본원 엄진영 강사님의 수업을 듣고 정리했습니다.


Mybatis3: select/insert/update/delete

1. Mapper.xml

SQL문은 태그 안에 작성한다.

  • select 태그에는 select 문장을, insert 태그에는 insert 문장을, update 태그에는 update 문장을, delete 태그에는 delete 문장을 작성한다.
  • 그런데 insert/update/delete 인 경우 insert/update/delete 구분없이 태그를 사용해도 된다.
    • 그 이유는 SQL문을 찾을 때 id 속성 값으로 찾기 때문이다. 하지만 유지보수의 일관성을 위해 SQL 문의 따라 적절한 태그를 사용하라!

select 태그

  • id : SQL문을 찾을 때 사용할 식별자이다.
  • resultType : select 결과를 저장할 클래스이름이나 별명이다.
  • 클래스이름을 경우 반드시 fully-qualified class name(패키지명을 포함한 클래스명)을 사용하라!
  • 값을 자바 객체에 넣는 규칙
    • 컬럼명과 일치하는 셋터를 호출한다.
    • 컬럼명 ⇒ set컬럼명()
    • 예) bno ==> setBno(값)
      • Board board = new Board(); board.setBno(rs.getNo(“bno”));
    • 만약 컬럼 이름에 해당하는 셋터를 못 찾으면 호출하지 않는다.
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  
<mapper namespace="BoardMapper">
  <select id="selectBoard" resultType="Board">
    select 
      board_id, <!-- Board.setBoard_id() 호출 -->
      title,    <!-- Board.setTitle() 호출 -->
      contents, <!-- Board.setContents() 호출 -->
      created_date, <!-- Board.setCreated_date() 호출 -->
      view_count    <!-- Board.setView_count() 호출 -->
    from x_board
  </select>
</mapper>
  • 위의 SQL문을 mybatis는 내부에서 다음과 같은 코드로 실행할 것이다.
  ArrayList<Board> list = new ArrayList<>();
        
  while (rs.next()) {
    Board board = new Board();
    board.setBoard_id(rs.getNo("board_id")); // 이런 셋터가 없다.
    board.setTitle(rs.getString("title")); // 이 셋터는 있다.
    board.setContents(rs.getString("contents")); // 이런 셋터가 없다.
    board.setCreated_date(rs.getDate("created_date")); // 이런 셋터가 없다.
    board.setView_count(rs.getDate("view_count")); // 이런 셋터가 없다.
    list.add(board);
  } 

  return list;

  // 그러나 안타깝게도 Board 클래스에는
  // 컬럼 이름과 일치하는 셋터가 딱 한개만 있다. title 컬럼이다.
  // 그 외 컬럼 값은 셋터가 없기 때문에 저장할 수 없다.
  // => 셋터의 이름(프로퍼티 이름)과 같은 이름으로 컬럼의 별명을 설정하라! 

2. 컬럼명과 프로퍼티 이름

  • 컬럼의 이름이 프로퍼티 이름과 다르다면, 셋터를 호출할 수 없다.
  • 컬럼 이름을 프로퍼티 이름과 일치시켜야만 정확하게 호출할 수 있다. select 문에서 컬럼의 별명을 프로퍼티명과 같게 하라!
  List<Board> list = sqlSession.selectList("BoardMapper.selectBoard");

  for (Board board : list) {
    System.out.printf("%d, %s, %s, %s, %d\n",
      board.getNo(),
      board.getTitle(),
      board.getContent(),
      board.getRegisteredDate(),
      board.getViewCount());
  }

컬럼명에 별명 지어주기 I

  • SQL문을 실행하는 태그 안에서 컬럼명에 일일이 별명을 붙인다.
<mapper namespace="BoardMapper">
  <select id="selectBoard" resultType="Board">
    select 
      board_id as no,     <!-- Board.setNo() 호출 -->
      title,              <!-- Board.setTitle() 호출 -->
      contents content,   <!-- Board.setContent() 호출 -->
      created_date as registeredDate, <!-- Board.setRegisteredDate() 호출 -->
      view_count viewCount            <!-- Board.setViewCount() 호출 -->
    from x_board
  </select>
</mapper>

컬럼명에 별명 지어주기 II

  • 태그를 이용하여 컬럼명에 별명을 붙여줄 수도 있다. 이 태그를 이용하면 컬럼명에 일일이 태그를 붙이지 않아도 된다.
<resultMap type="자바클래스명" id="식별자">
	<id column="테이블컬럼명" property="자바 프로퍼티명"/>
	<result column="테이블컬럼명" property="자바 프로퍼티명"/>
	<result column="테이블컬럼명" property="자바 프로퍼티명"/>
</resultMap>
  • 컬럼명과 자바 객체의 프로퍼티 명을 미리 연결한다.
    • type: 자바 객체의 클래스명 또는 별명
    • id: 연결 정보를 가리키는 식별자
  • 컬럼명과 자바 객체의 프로퍼티명을 연결한다.
    • column=”테이블 컬럼명”
    • property=”자바 객체의 프로퍼티명”
    • 시작태그와 끝태그 사이에 추가 내용이 없다면 끝태그를 생략하고 대신에 시작태그의 끝에 /를 붙인다.
    • 주의! primary key 컬럼인 경우 id 태그를 사용하라!
    • 컬럼명과 프로퍼티명이 같을 때는 result 로 지정하지 않아도 된다.
  • resultMap으로 정의한 연결 정보를 사용하려면, sql 문에서 resultMap="컬럼과 프로퍼티의 연결을 정의한 resultMap 아이디"를 설정하면 된다.
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  
<mapper namespace="BoardMapper">
  <resultMap type="Board" id="BoardMap">

	  <!-- board_id는 pk 컬럼이기 때문에 id 태그를 사용했다. -->
    <id column="board_id" property="no"/>
    
    <!--  title은 컬럼명과 프로퍼티명이 같기 때문에 result로 지정하지 않았다. -->
    <result column="contents" property="content"/>
    <result column="created_date" property="registeredDate"/>
    <result column="view_count" property="viewCount"/>
  </resultMap>
  
  <select id="selectBoard" resultMap="BoardMap">
    select 
      board_id, <!-- BoardMap의 연결정보를 참조하기 때문에 별명을 주지 않아도 된다. -->
      title, 
      contents, 
      created_date,
      view_count 
    from x_board
  </select>
</mapper>

3. 파라미터

SQL문에 값 삽입하기

  • SQL을 실행할 때 파라미터 값을 전달하려면 두 번째 파라미터로 전달해야 한다.
  • SQL 문에 값 삽입하기
    • in-parameter 지정하기
      • parameterType에 지정된 객체의 프로퍼티 명을 사용하여 값을 삽입한다.

        예) #{프로퍼티명}

      • paramterType이 primitive/String/wrapper class 인 경우 아무 이름을 적어도 된다.

        예) #{아무이름}

      • parameterType이 Map 객체인 경우는 Map에 저장된 값의 key를 적는다.

        예) #{key}

    • parameterType에 지정할 수 있는 타입
원래 명칭 내장 별칭
int _int
int _integer
short _short
long _long
double _double
float _float
byte _byte
boolean _boolean
Integer int
Integer integer
Double double
Float float
String string
Byte byte
Long long
Short short
Date date
Object object
List list
ArrayList arraylist
Collection collection
Map map
HashMap hashmap
Iterator iterator

한 개의 값 전달하기

  // 예) 특정 번호 이상의 게시물을 가져온다.
  List<Board> list = sqlSession.selectList(
    "BoardMapper.selectBoard1", 15);

  for (Board board : list) {
    System.out.printf("%d, %s, %s, %s\n",
      board.getNo(),
      board.getTitle(),
      board.getContent(),
      board.getRegisteredDate());
  }

<!-- selectList(sqlid, int) --> 
  <select id="selectBoard1" 
          resultMap="BoardMap" 
          parameterType="int">
  <![CDATA[
    select 
      board_id,
      title, 
      contents, 
      created_date,
      view_count 
    from x_board
    where board_id > #{ohora}
  ]]>
  </select>
  • CDATA는 Character Data 섹션의 의미이다. 부등호나 비교연산자 등이 캐릭터로 처리된다.

여러 개의 값 전달하기

  • 여러 개의 값을 전달해야 한다면, Map 객체에 담아 전달하라!
  // 예) 게시글 제목에 ohora를 포함한 게시글을 찾는다.
  List<Board> list = sqlSession.selectList(
          "BoardMapper.selectBoard2", "%ohora%");

  for (Board board : list) {
    System.out.printf("%d, %s, %s, %s\n",
      board.getNo(),
      board.getTitle(),
      board.getContent(),
      board.getRegisteredDate());
  }
<!-- selectList(sqlid, String) -->
  <select id="selectBoard2" 
          resultMap="BoardMap" 
          parameterType="string">
    select 
      board_id,
      title, 
      contents, 
      created_date,
      view_count 
    from x_board
    where title like #{haha}
  </select>
  • 여러 개의 값을 전달해야 할 때 도메인 객체(ex: Board, Lesson 등)에 담아 전달할 수도 있다.
  // 예) 페이징 처리를 위한 시작 인덱스와 개수를 파라미터로 넘겨라.
  HashMap<String, Object> params = new HashMap<>();
  params.put("startIndex", 6);
  params.put("size", 3);

  List<Board> list = sqlSession.selectList(
    "BoardMapper.selectBoard3", params);

  for (Board board : list) {
    System.out.printf("%d, %s, %s, %s\n",
      board.getNo(), 
      board.getTitle(),
      board.getContent(),
      board.getRegisteredDate());
  }
<!-- selectList(sqlid, HashMap)
  => Map에서 값을 꺼낼 때는 값을 저장할 때 사용한 key를 이용한다.
  => #{key}
 -->
  <select id="selectBoard3" 
          resultMap="BoardMap" 
          parameterType="map">
    select 
      board_id,
      title, 
      contents, 
      created_date,
      view_count 
    from x_board
    order by board_id desc
    limit #{startIndex}, #{size}
  </select>

4. #{}와 ${}

#{}

  • #{}는 을 삽입할 때 사용한다.
  • 파라미터 값을 SQL에 그대로 삽입하려면 ${} 문법을 사용해야 한다.
  // 예) 파라미터로 컬럼 이름을 넘겨주면
  //     해당 컬럼의 값을 오름차순으로 정렬한다.
  List<Board> list = sqlSession.selectList(
    "BoardMapper.selectBoard1", "title");
    // => 현재 selectBoard1에서는 #{}을 사용했기 때문에
    //    order by가 정상적으로 적용되지 못했다. (SQL 오류는 아니다.)
    //    mariaDB는 order by 절에 문자열이 오면 무시한다. 

  for (Board board : list) {
    System.out.printf("%d, %s, %s, %s\n",
      board.getNo(),
      board.getTitle(),
      board.getContent(),
      board.getRegisteredDate());
  }
<!-- selectList(sqlid, string) --> 
  <select id="selectBoard1" 
          resultMap="BoardMap" 
          parameterType="string">
    select 
      board_id,
      title, 
      contents, 
      created_date,
      view_count 
    from x_board
    order by #{colname} asc
  </select>

${}

  • ${파라미터명} : ${} 문법은 값을 SQL 문에 그대로 삽입한다.
  • 단점
    • 외부의 값을 이용하여 SQL 문장을 만들 수 있다.
    • 하지만 SQL 삽입 공격에 취약하다!
  // 예) 파라미터로 컬럼 이름을 넘겨주면
  //     해당 컬럼의 값을 오름차순으로 정렬한다.
  List<Board> list = sqlSession.selectList(
    "BoardMapper.selectBoard2", "contents");
    
  for (Board board : list) {
    System.out.printf("%d, %s, %s, %s\n",
      board.getNo(),
      board.getTitle(),
      board.getContent(),
      board.getRegisteredDate());
  }
<!-- selectList(sqlid, string) --> 
  <select id="selectBoard2" 
          resultMap="BoardMap" 
          parameterType="string">
    select 
      board_id,
      title, 
      contents, 
      created_date,
      view_count 
    from x_board
    order by ${colname} asc
  </select>

SQL문을 만들어 전달하기

  • mybatis에 SQL문을 만들어 전달할 수 있다.
  • SQL Mapper 에서는 ${} 문법으로 SQL 문을 받는다.
  • 단, 사용자가 입력한 값을 그대로 전달한다면 SQL 삽입 공격에 노출될 수 있기 때문에 위험하다.
  • 사용자가 입력한 값을 그대로 전달하지 않고, 개발자가 지정한 값을 전달한다면 안전하게 사용할 수 있다.
  List<Board> list = sqlSession.selectList(
    "BoardMapper.selectBoard3", "where title like '%ok%'");

  for (Board board : list) {
    System.out.printf("%d, %s, %s, %s\n",
      board.getNo(),
      board.getTitle(),
      board.getContent(),
      board.getRegisteredDate());
  }
<!-- selectList(sqlid, string) -->
  <select id="selectBoard3" 
          resultMap="BoardMap" parameterType="string">
    select 
      board_id,
      title, 
      contents, 
      created_date,
      view_count 
    from x_board
    ${sql}
  </select>

5. Insert

autocommit

  • insert/update/delete 과 같이 데이터를 변경하는 작업은 위험하기 때문에 DBMS의 임시 메모리에 그 작업 결과를 보관한다.
  • 클라이언트에서 최종적으로 변경을 허락해야만(commit을 해야만) 진짜 테이블에 값을 반영한다.
  • mybatis에서는 autocommit이 기본으로 false이다.

commit & rollback

  • commit : 임시 메모리에 저장된 작업 결과를 실제 테이블에 반영시키는 명령
  • rollback : 임시 메모리에 저장된 작업 결과를 취소하는 명령

파라미터로 일반 객체 전달하기

  • SQL문에서 일반 객체를 파라미터로 받을 수 있다.
  • parameterType=”클래스명 또는 별명”
  • 일반 객체에서 값을 꺼내려면 프로퍼티(getter)명을 지정해야 한다.
  • 예)
    • #{title} ⇒ getTitle()의 리턴 값을 의미
    • #{content} ⇒ getContent()의 리턴 값을 의미
  // Board 객체에 값을 저장하여 전달하기
  //  => 단 값을 꺼낼 수 있도록 겟터(프로퍼티)가 있어야 한다.
  Board board = new Board();
  board.setTitle("제목입니다.");
  board.setContent("내용입니다.");

  int count = sqlSession.insert("BoardMapper.insertBoard", board);
  System.out.println(count);

  // Mybatis가 Board 객체의 내용을 테이블에 저장한 뒤에도
  // Board 객체의 번호는 계속 0인 채로 있다.
  System.out.printf("번호: %d\n", board.getNo());
  System.out.printf("제목: %s\n", board.getTitle());
  System.out.printf("내용: %s\n", board.getContent());

  // mybatis에서는 autocommit이 기본으로 false이다.
  // commit 명령을 내리지 않으면 insert/update/delete을 테이블에 반영하지 않는다.
  // close() 할 때 취소된다.
  sqlSession.commit();

<!-- insert(sqlId, Board) -->
  <insert id="insertBoard" parameterType="Board">
    insert into x_board(title,contents,created_date) 
    values(#{title},#{content},now())
  </insert>

insert후 자동 증가된 PK값 가져오기

  • INSERT를 실행한 후 자동으로 생성된 PK 값을 가져오려면 파라미터로 넘겨준 객체에 담아 달라고 요청하라!
  Board board = new Board();
  board.setTitle("제목입니다.");
  board.setContent("내용입니다.");

  sqlSession.insert("BoardMapper.insertBoard", board);

  // mybatis는 insert를 실행한 후에 자동 증가된 PK 값(board_id 컬럼의 값)을
  // 다시 board 객체에 담아줄 것이다.
  System.out.printf("번호: %d\n", board.getNo());
  System.out.printf("제목: %s\n", board.getTitle());
  System.out.printf("내용: %s\n", board.getContent());

  sqlSession.commit();
  <insert id="insertBoard" parameterType="Board"
          useGeneratedKeys="true" keyColumn="board_id" keyProperty="no">
    insert into x_board(title,contents,created_date) 
    values(#{title},#{content},now())
  </insert>

6. Update

  • update SQL을 실행할 때 update 태그 대신 insert/delete 태그를 사용해도 된다. mybatis는 SQL을 찾을 때 id 값으로 찾기 때문이다.
  • 그러나 유지보수를 위해 가능한 일관된 이름을 사용하라. 즉 insert SQL 문은 insert 태그에 넣고, update SQL 문은 update 태그에 넣어라.
  // 변경할 데이터를 객체에 담아서 넘긴다.
  Board board = new Board();
  board.setNo(15); // 저장된 데이터에 따라서 숫자 다르게 넣기!
  board.setTitle("aaaa");
  board.setContent("bbbb");

  sqlSession.update("BoardMapper.updateBoard", board);
  sqlSession.commit();
  <insert id="updateBoard" parameterType="Board">
    update x_board set
      title=#{title},
      contents=#{content}
    where board_id=#{no}
  </insert>

7. Delete

  • 테이블을 삭제할 때, 자식 테이블이 있다면 자식 테이블의 데이터를 지운 후 부모 테이블을 지운다.
  // 먼저 자식 테이블의 데이터를 지운다.
  sqlSession.delete("BoardMapper.deleteBoardFile", 18);

  // 그 후 부모 테이블의 데이터를 지운다.
  sqlSession.delete("BoardMapper.deleteBoard", 18);

  sqlSession.commit();
  <delete id="deleteBoard" parameterType="int">
    delete from x_board
    where board_id=#{value}
  </delete>
  
  <delete id="deleteBoardFile" parameterType="int">
    delete from x_board_file
    where board_id=#{value}
  </delete>

태그:

카테고리:

업데이트: