본문 바로가기

학원/복기

[MyBatis] 자동매핑&수동매핑 예제 / selectKey,counstructor 엘리먼트

테이블 및 시퀀스 생성
 
MYCOMMENT 테이블 : 게시글을 저장하기 위한 테이블 
MYCOMMENT_SEQ : MYCOMMENT 테이블의 COMMENT_NO 컬럼값으로 저장될 자동 증가값 

/*테이블 생성*/
create table mycomment(comment_no number primary key, comment_id varchar2(50)
    , comment_content varchar2(100), comment_date date);

/*시퀀스 생성*/
create sequence mycomment_seq;

DTO 생성 
 
[MyComment1] 클래스 선언 - 테이블의 컬러명과 같은 이름으로 클래스의 필드명을 작성 (자동 매핑)
→ 스네이크 표기법으로 작성된 컬럼명은 카멜 표기법의 필드명으로 작성해준다.
 

package xyz.itwill.dto;

public class MyComment1 {
	private int commentNo;
	private String commentId;
	private String commentContent;
	private String commentDate;
	
	public MyComment1() {
		// TODO Auto-generated constructor stub
	}

	public int getCommentNo() {
		return commentNo;
	}

	public void setCommentNo(int commentNo) {
		this.commentNo = commentNo;
	}

	public String getCommentId() {
		return commentId;
	}

	public void setCommentId(String commentId) {
		this.commentId = commentId;
	}

	public String getCommentContent() {
		return commentContent;
	}

	public void setCommentContent(String commentContent) {
		this.commentContent = commentContent;
	}

	public String getCommentDate() {
		return commentDate;
	}

	public void setCommentDate(String commentDate) {
		this.commentDate = commentDate;
	}
}

 


mpper 파일을 2개 만든다. (xml매퍼, interface 매퍼)
 
먼저 xml 매퍼를 만들어보자
 
 
[MyCommentMapper.xml]
 
resultType 속성을 사용하여 검색행이 Java 객체로 제공되도록 자동 매피 처리할 땐, 검색행의 컬럼명과 resultType 속성값으로 설정된 클래스의 필드명이 같도록 작성해야 한다. 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="xyz.itwill.mapper.MyCommentMapper">
	<insert id="insertComment1" parameterType="MyComment1">
		insert into mycomment values(mycomment_seq.nextval, #{commentId}, #{commentContent}, sysdate)
	</insert>
	
	<!-- resultType 속성을 사용하여 검색행이 Java 객체로 제공되도록 자동 매핑 처리 -->
	<!-- => 검색행의 컬럼명과 resultType 속성값으로 설정된 클래스의 필드명이 같도록 작성 -->
	<select id="selectCommentList1" resultType="MyComment1">
		select comment_no, comment_id, comment_content, comment_date from mycomment order by comment_no desc
	</select>
</mapper>

 
 
MyCommentMapper 인터페이스도 선언

package xyz.itwill.mapper;

import java.util.List;

import xyz.itwill.dto.MyComment1;

public interface MyCommentMapper {
	int insertComment1(MyComment1 comment);
	List<MyComment1> selectCommentList1();
}

이제 DAO를 선언하자
AbstractSession을 상속받는다.
 
[MyCommentDAO]
 

package xyz.itwill.dao;

import java.util.List;

import org.apache.ibatis.session.SqlSession;

import xyz.itwill.dto.MyComment1;
import xyz.itwill.mapper.MyCommentMapper;

public class MyCommentDAO extends AbstractSession {
	private static MyCommentDAO _dao;

	private MyCommentDAO() {
		// TODO Auto-generated constructor stub
	}
	
	static {
		_dao=new MyCommentDAO();
	}
	
	public static MyCommentDAO getDAO() {
		return _dao;
	}
	
	public int insertComment1(MyComment1 comment) {
		SqlSession sqlSession=getSqlSessionFactory().openSession(true);
		try {
			return sqlSession.getMapper(MyCommentMapper.class).insertComment1(comment);
		} finally {
			sqlSession.close();
		}
	}
	
	public List<MyComment1> selectCommentList1() {
		SqlSession sqlSession=getSqlSessionFactory().openSession(true);
		try {
			return sqlSession.getMapper(MyCommentMapper.class).selectCommentList1();
		} finally {
			sqlSession.close();
		}
	}
}

동작이 잘 되는지 확인하기 위해 jsp 문서 작성
 
[commentInsert1.jsp]

<%@page import="xyz.itwill.dao.MyCommentDAO"%>
<%@page import="xyz.itwill.dto.MyComment1"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
	MyComment1 comment1=new MyComment1();
	comment1.setCommentId("abc");
	comment1.setCommentContent("첫번째 게시글입니다.");
	MyCommentDAO.getDAO().insertComment1(comment1);
	
	MyComment1 comment2=new MyComment1();
	comment2.setCommentId("xyz");
	comment2.setCommentContent("두번째 게시글입니다.");
	MyCommentDAO.getDAO().insertComment1(comment2); 
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>MYBATIS</title>
</head>
<body>
	<h1>게시글 등록</h1>
	<hr>
	<h3>게시글이 성공적으로 삽입 되었습니다.</h3>
</body>
</html>

 

[commentInsert1.jsp]를 실행하여 게시글을 삽입했다.
 
 


selectKey 

 

insert 엘리먼트의 하위 엘리먼트인 selectKey 엘리먼트를 사용해보자
 
selectKey : SELECT 명령의 검색결과값을 제공받아 insert 엘리먼트의 parameterType 속성값으로 설정된 객체의 필드값으로 저장하기 위한 엘리먼트 - insert 엘리먼트에 종속된 엘리먼트 
→ 자동 증가값 또는 난수값을 SELECT 명령으로 검색하여 객체 필드에 저장해 INSERT 명령에서 사용하기 위해서 작성한다.
 - resultType 속성 : SELECT 명령으로 검색된 결과값을 제공받기 위한 Java 자료형을 속성값으로 설정한다.
 → Java 자료형 대신 typeAlias 엘리먼트로 제공된 별칭을 사용할 수 있다.
 - keyProperty 속성 : insert 엘리먼트의 parameterType 속성값으로 설정된 클래스의 필드명을 속성값으로 설정한다.
 - order 속성 : [BEFORE] 또는 [AFTER] 중 하나를 속성값으로 설정한다.
 
 
[MyCommentMapper.xml]

<insert id="insertComment2" parameterType="MyComment1">
		
	<selectKey resultType="int" keyProperty="commentNo" order="BEFORE">
		select mycomment_seq.nextval from dual
	</selectKey>
	insert into mycomment values(#{commenNo}, #{commentId}, #{commentContent}, sysdate)
</insert>

 
DAO와 인터페이스 매퍼에도 값을 추가해주자.
 

package xyz.itwill.dao;

import java.util.List;

import org.apache.ibatis.session.SqlSession;

import xyz.itwill.dto.MyComment1;
import xyz.itwill.mapper.MyCommentMapper;

public class MyCommentDAO extends AbstractSession {
	private static MyCommentDAO _dao;

	private MyCommentDAO() {
		// TODO Auto-generated constructor stub
	}
	
	static {
		_dao=new MyCommentDAO();
	}
	
	public static MyCommentDAO getDAO() {
		return _dao;
	}
	
	public int insertComment1(MyComment1 comment) {
		SqlSession sqlSession=getSqlSessionFactory().openSession(true);
		try {
			return sqlSession.getMapper(MyCommentMapper.class).insertComment1(comment);
		} finally {
			sqlSession.close();
		}
	}
	
	public int insertComment2(MyComment1 comment) {
		SqlSession sqlSession=getSqlSessionFactory().openSession(true);
		try {
			return sqlSession.getMapper(MyCommentMapper.class).insertComment2(comment);
		} finally {
			sqlSession.close();
		}
	}
	
	public List<MyComment1> selectCommentList1() {
		SqlSession sqlSession=getSqlSessionFactory().openSession(true);
		try {
			return sqlSession.getMapper(MyCommentMapper.class).selectCommentList1();
		} finally {
			sqlSession.close();
		}
	}
}

 

package xyz.itwill.mapper;

import java.util.List;

import xyz.itwill.dto.MyComment1;

public interface MyCommentMapper {
	int insertComment1(MyComment1 comment);
	int insertComment2(MyComment1 comment);
	List<MyComment1> selectCommentList1();
}

 


jsp 문서 만들어서 삽입처리 해보자
 

<%@page import="xyz.itwill.dao.MyCommentDAO"%>
<%@page import="xyz.itwill.dto.MyComment1"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
	MyComment1 comment3=new MyComment1();
	comment3.setCommentId("opq");
	comment3.setCommentContent("세번째 게시글입니다.");
	MyCommentDAO.getDAO().insertComment2(comment3);
	
	MyComment1 comment4=new MyComment1();
	comment4.setCommentId("abc");
	comment4.setCommentContent("네번째 게시글입니다.");
	MyCommentDAO.getDAO().insertComment2(comment4);
%>    
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>MYBATIS</title>
</head>
<body>
	<h1>게시글 등록</h1>
	<hr>
	<h3>게시글이 성공적으로 삽입 되었습니다.</h3>
</body>
</html>

 

 


commentListSelect1.jsp 문서를 만들어보자
 

<%@page import="xyz.itwill.dao.MyCommentDAO"%>
<%@page import="xyz.itwill.dto.MyComment1"%>
<%@page import="java.util.List"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
	List<MyComment1> commentList=MyCommentDAO.getDAO().selectCommentList1();
%>    
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>MYBATIS</title>
<style type="text/css">
table {
	border: 1px solid black;
	border-collapse: collapse;
}

td {
	border: 1px solid black;
	text-align: center;
	padding: 3px;
}

.no { width: 100px; }
.name { width: 150px; }
.content { width: 250px; }
.date { width: 200px; }
</style>
</head>
<body>
	<h1>게시글 목록</h1>
	<hr>
	<table>
		<tr>
			<td class="no">게시글번호</td>
			<td class="name">게시글작성자</td>
			<td class="content">게시글내용</td>
			<td class="date">게시글작성일</td>
		</tr>
		<% for(MyComment1 comment : commentList) { %>
		<tr>
			<td><%=comment.getCommentNo() %></td>
			<td><%=comment.getCommentId() %></td>
			<td><%=comment.getCommentContent() %></td>
			<td><%=comment.getCommentDate() %></td>
		</tr>
		<% } %>
	</table>
</body>
</html>


이번엔 MyComment2 클래스를 선언해보자 (DTO 클래스)
MyComment2 클래스는 테이블의 컬럼명과 다른 이름으로 클래스의 필드명을 작성하여 수동 매핑 할것이다.

package xyz.itwill.dto;

//테이블의 컬럼명과 다른 이름으로 클래스의 필드명을 작성한 클래스
public class MyComment2 {
	private int no;
	private String id;
	private String content;
	private String date;
	
	public MyComment2() {
		// TODO Auto-generated constructor stub
	}

	public MyComment2(int no, String id) {
		super();
		this.no = no;
		this.id = id;
	}

	public MyComment2(int no, String id, String content, String date) {
		super();
		this.no = no;
		this.id = id;
		this.content = content;
		this.date = date;
	}

	public int getNo() {
		return no;
	}

	public void setNo(int no) {
		this.no = no;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getContent() {
		return content;
	}

	public void setContent(String content) {
		this.content = content;
	}

	public String getDate() {
		return date;
	}

	public void setDate(String date) {
		this.date = date;
	}
}

 
 


 
 
 
xml 매퍼에 매핑 처리 

<select id="selectCommentList2" resultType="MyComment2">
	select comment_no, comment_id, comment_content, comment_date from mycomment order by comment_no desc
</select>

 
인터페이스 매퍼에 추가

package xyz.itwill.mapper;

import java.util.List;

import xyz.itwill.dto.MyComment1;
import xyz.itwill.dto.MyComment2;

public interface MyCommentMapper {
	int insertComment1(MyComment1 comment);
	int insertComment2(MyComment1 comment);
	List<MyComment1> selectCommentList1();
	List<MyComment2> selectCommentList2();
}

 
DAO 클래스 변경
 

	public List<MyComment2> selectCommentList2() {
		SqlSession sqlSession=getSqlSessionFactory().openSession(true);
		try {
			return sqlSession.getMapper(MyCommentMapper.class).selectCommentList2();
		} finally {
			sqlSession.close();
		}
	}

 


jsp 문서로 확인해보기

<%@page import="xyz.itwill.dao.MyCommentDAO"%>
<%@page import="xyz.itwill.dto.MyComment2"%>
<%@page import="java.util.List"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
	List<MyComment2> commentList=MyCommentDAO.getDAO().selectCommentList2();
%>    
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>MYBATIS</title>
<style type="text/css">
table {
	border: 1px solid black;
	border-collapse: collapse;
}

td {
	border: 1px solid black;
	text-align: center;
	padding: 3px;
}

.no { width: 100px; }
.name { width: 150px; }
.content { width: 250px; }
.date { width: 200px; }
</style>
</head>
<body>
	<h1>게시글 목록</h1>
	<hr>
	<table>
		<tr>
			<td class="no">게시글번호</td>
			<td class="name">게시글작성자</td>
			<td class="content">게시글내용</td>
			<td class="date">게시글작성일</td>
		</tr>
		<% for(MyComment2 comment : commentList) { %>
		<tr>
			<td><%=comment.getNo() %></td>
			<td><%=comment.getId() %></td>
			<td><%=comment.getContent() %></td>
			<td><%=comment.getDate() %></td>
		</tr>
		<% } %>
	</table>
</body>
</html>

 
 
실행해보면 에러가 발생한다. (NullPointerException)
 
검색행의 컬럼명과 resultType 속성값으로 설정된 클래스의 필드명이 모두 다른 경우 resultType 속성값으로 설정된 클래스의 객체 대신 NULL을 제공하기 때문이다.
 


이를 해결하기 위해서는 자동매핑 처리해주어야한다.
 
자동매핑 처리 하기 위해서는 검색행의 컬럼명을 resultType 속성값으로 설정된 클래스의 필드명과 같도록 ColumnAlias 기능을 사용하여 검색해주면 된다.
식별자의 별칭으로 사용하기 부적절한 단어(예제에서 date)는 " " 기호를 사용하여 표현하면 된다. 
 
[MyCommentMapper.xml]

<select id="selectCommentList2" resultType="MyComment2">
	select comment_no "no", comment_id "id", comment_content "comment", comment_date "date"
    	from mycomment order by comment_no desc
</select>


수동매핑

 

 resultMap 엘리먼트를 이용하여 검색행의 컬럼값이 저장되도록 설정하여 검색행을 Java 객체로 제공할 수 있다.
→ type 속성값으로 설정된 클래스의 기본 생성자를 이용하여 객체를 생성하고, id 엘리먼트 또는 result 엘리먼트를 사용하여 객체의 Setter 메소드를 호출해 검색행의 컬럼값으로 필드값을 변경한다. 
(수동매핑할 는 resultMap을 사용하고, 자동매핑할때는 resultType을 사용한다.)

→ resultMap 엘리먼트의 id 엘리먼트 또는 result 엘리먼트는 0 개이상 
 
select 엘리먼트의 resultMap 속성을 사용하여 검색행을 Java 객체로 수동 매핑하여 제공한다.
 

[MyCommentMapper.xml]

	 <resultMap type="MyComment2" id="myComment2ResultMap">
	 	<id column="comment_no" property="no"/>
	 	<result column="comment_id" property="id"/>
	 	<result column="comment_content" property="content"/>
	 	<result column="comment_date" property="date"/>
	 </resultMap>
	 
	 <!-- select 엘리먼트의 resultMap 속성을 사용하여 검색행을 Java 객체로 수동 매핑하여 제공 -->
	 <select id="selectCommentList2" resultMap="myComment2ResultMap">
	 	select comment_no, comment_id, comment_content, comment_date from mycomment order by comment_no desc
	 </select>

결과가 잘 출력되는 것을 확인할 수 있다.


constructor 

 

resultMap 을 consturctor 엘리먼트를 이용해 다시 만들어보자

 

constructor 엘리먼트를 사용하면 type 속성값으로 설정된 클래스의 매개변수가 작성된 생성자로 객체를 생성하고 생성자 매개변수에 검색행의 컬럼값을 전달하여 객체 필드에 저장할 수 있도록 만들 수 있다.

→ resultMap 엘리먼트의 하위 엘리먼트로 constructor 엘리먼트는 0개 또는 1개만 작성이 가능하다.

 

constructor : resultMap 엘리먼트의 type 속성값으로 설정된 클래스의 생성자를 이용하여 매핑 처리하기 위한 정보를 제공하는 엘리먼트

→ 하위 엘리먼트 : idArg 엘리먼트, arg 엘리먼트

생성자의 매개변수의 갯수와 하위 엘리먼트의 갯수 및 자료형이 반드시 같도록 작성해야 한다.

 

idArg : 검색행의 컬럼값을 생성자 매개변수에 전달하기 위한 엘리먼트 

PK 제약조건이 설정된 컬럼값을 제공받아 생성자 매개변수에 저장한다.

 - column 속성 : 검색행의 컬럼명을 속성값으로 설정한다.

 - javaType 속성 : 검색행의 컬럼값을 저장할 매개변수의 Java 자료형을 속성값으로 설정한다. Java 자료형 대신 typeAlias로 설정된 별칭 사용이 가능하다. 

arg : 검색행의 컬럼값을 생성자 매개변수에 전달하기 위한 엘리먼트

 * javaType에 int를 작성하면 Integer 클래스가 생성되므로 반드시 _int 라고 작성해 원시형으로 표시해준다.

내장 별칭 타입 참고 : https://mybatis.org/mybatis-3/ko/configuration.html#typeAliases
 

	<resultMap type="MyComment2" id="myComment2ConstructorResultMap"><!-- id는 중복되면 안된다 -->
	 	<constructor>
	 		<idArg column="comment_no" javaType="_int"/> <!-- 원시형을 사용하기 위해서 _int라고 작성 -->
	 		<arg column="comment_id" javaType="string"/>
	 		<arg column="comment_content" javaType="string"/>
	 		<arg column="comment_date" javaType="string"/>
	 	</constructor>
	 </resultMap>
	 
	  <select id="selectCommentList2" resultMap="myComment2ConstructorResultMap">
	 	select comment_no, comment_id, comment_content, comment_date from mycomment order by comment_no desc
	 </select>

 

constructor 엘리먼트와 id 엘리먼트(또는 result 엘리먼트)를 같이 사용하여 매핑 정보를 제공하는 것이 가능하다.

(매개변수가 2개인 생성자를 이용했다.)

 	<resultMap type="MyComment2" id="myComment2ConstructorResultMap"><!-- id는 중복되면 안된다 -->
	 	<constructor>
	 		<idArg column="comment_no" javaType="_int"/> <!-- 원시형을 사용하기 위해서 _int라고 작성 -->
	 		<arg column="comment_id" javaType="string"/>
	 		<!--  
	 		<arg column="comment_content" javaType="string"/>
	 		<arg column="comment_date" javaType="string"/>
	 		-->
	 	</constructor>
	 	<result column="comment_content" property="content"/>
	 	<result column="comment_date" property="date"/>
	 </resultMap>