Mybatis3: select/insert/update/delete
비트캠프 서초본원 엄진영 강사님의 수업을 듣고 정리했습니다.
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에 지정할 수 있는 타입
- in-parameter 지정하기
| 원래 명칭 | 내장 별칭 |
|---|---|
| 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>