관리 메뉴

거니의 velog

(10) 마이바티스 프레임워크 사용하기 1 본문

Java_Spring Framework part1

(10) 마이바티스 프레임워크 사용하기 1

Unlimited00 2023. 10. 28. 16:43

1. 마이바티스란?

* 애플리케이션의 규모가 작을 때는 JDBC를 이용해 충분히 개발할 수 있었다. 그러나 인터넷 사용자가 폭발적으로 증가하고 애플리케이션의 기능이 복잡해짐에 따라 기존의 JDBC로 개발하는 데는 한계가 드러나게 되었다.

* 기존 JDBC로 개발할 경우 반복적으로 구현해야 할 SQL 문도 많을 뿐만 아니라 SQL 문도 복잡하다. 따라서 자연스럽게 마이바티스(MyBatis)나 하이버네이트와 같은 데이터베이스 연동 관련 프레임워크가 등장하게 되었다. 이번에는 마이바티스 프레임워크에 대해 알아볼 것이다. 우선 마이바티스를 왜 사용하게 되었는지 그 등장 배경부터 알아보자.

* 기존의 JDBC를 연동하려면 다음과 같은 과정을 거쳐야 한다.

connection -> Statement 객체 생성 -> SQL문 전송 -> 결과 반환 -> close

* 이 방식의 단점은 SQL문이 프로그래밍 코드에 섞여 코드를 복잡하게 만든다는 것이다. 이 방법을 개선해 SQL 문의 가독성을 높여 사용하기 편하게 만든 것이 바로 마이바티스 프레임워크이다.

* 다음 코드는 쇼핑몰에서 자바의 JDBC 기능을 이용해 상품 정보를 추가하는 insert 문이다. 문자열로 SQL 문을 만들어 사용하는 방식이다.

[AdminDAO.java]

...

public void addGoods(GoodsVO goodsVO) throws SQLException {
	Connection conn = dataFactory.getConnection();
    Statement stmt = conn.createStatement();
    String query = "insert into t_Goods_info (goods_id, " +
    			"goods_sort, " +
                    "goods_title, " +
                    "goods_writer, " +
                    "goods_publisher, " +
                    "goods_price, " +
                    "goods_sales_price, " +
                    "goods_point, " +
                    "goods_published_date, " +
                    "goods_total_page, " +
                    "goods_isbn, " +
                    "goods_delivery_price, " +
                    "goods_delivery_date, " +
                    "goods_type, " +
                    "goods_writer_intro, " +
                    "goods_intro, " +
                    "goods_publisher_comment, " +
                    "goods_recommendation, " +
                    "goods_contents_order)";
           query += values('"+
           		"goodsVO.getGoods_id()+"', '"+
                    "goodsVO.getGoods_sort()+"', '"+
                    "goodsVO.getGoods_title()+"', '"+
                    "goodsVO.getGoods_writer()+"', '"+
                    "goodsVO.getGoods_publisher()+"', '"+
                    "goodsVO.getGoods_price()+"', '"+
                    "goodsVO.getGoods_sales_price()+"', '"+
                    "goodsVO.getGoods_point()+"', '"+
                    "goodsVO.getGoods_published_date()+"', '"+
                    "goodsVO.getGoods_page_total()+"', '"+
                    "goodsVO.getGoods_isbn()+"', '"+
                    "goodsVO.getGoods_delivery_price()+"', '"+
                    "goodsVO.getGoods_delivery_date()+"', '"+
                    "goodsVO.getGoods_type()+"', '"+
                    "goodsVO.getGoods_writer_intro()+"', '"+
                    "goodsVO.getGoods_intro()+"', '"+
                    "goodsVO.getGoods_publisher_comment()+"', '"+
                    "goodsVO.getGoods_recommandation()+"', '"+
                    "goodsVO.getGoods_contents_order()+"')";
                    System.out.println(query);
                    stmt.executeUpdate(query);
                    String goods_id = goodsVO.getGoods_id();
}

...

* 애플리케이션의 기능이 복잡해지면 해당 기능과 관련된 SQL문의 길이가 보통 몇 십 줄씩 된다. 그런데 길이가 긴 SQL문을 위 코드처럼 다시 문자열로 만들어서 사용한다면 상당히 불편하다. 그뿐만 아니라 SQL문 실행 시 오류도 많이 발생하고 유지보수에도 문제가 많이 발생한다.

* 하지만 마이바티스를 사용하면 복잡한 SQL문이라도 SQL Developer 같은 도구에서 SQL문을 사용하는 것처럼 표준화된 방법으로 사용할 수 있다. 아래의 코드처럼 개발자가 작성하기 쉬울 뿐 아니라 코드 가독성도 좋다.

[SQL Developer에서 실행한 SQL 문]

insert into t_shopping_order (order_id
				, member_id
                                , goods_id
                                , goods_title
                                , order_goods_qty
                                , order_total_price
                                , orderer_name
                                , receiver_name
                                , receiver_hp1
                                , receiver_hp2
                                , receiver_hp3
                                , delivery_address
                                , delivery_message
                                , delivery_method
                                , smssts_yn
                                , gift_wrapping
                                , pay_method
                                , card_com_name
                                , card_pay_month
                                , pay_ordered_hp_num)
                      values ('1'
                      		, 'lee'
                            , '19'
                            , '아이디어 구현 중심 자바'
                            , 1
                            , 27000
                            , '이병승'
                            , '이병승'
                            , '011'
                            , '1212'
                            , '6767'
                            , '우편번호:05762<br>도로명주소:서울 송파구 강남빌딩 301호'
                            , '택배 기사님께 전달할 메시지를 남겨주세요.'
                            , '일반택배'
                            , 'no'
                            , '신용카드<br>카드사:삼성<br>할부개월수:3개월
                            , '삼성'
                            , '3개월'
                            , '해당 없음');

* 마이바티스 프레임워크의 특징은 다음과 같다.

* SQL 실행 결과를 자바 빈즈 또는 Map 객체에 매핑해 주는 Persistence 솔루션으로 관리한다.
  즉, SQL을 소스 코드가 아닌 XML로 분리한다.

* SQL문과 프로그래밍 코드를 분리해서 구현한다.

* 데이터소스(DaraSource) 기능과 트랜잭션 처리 기능을 제공한다.

(1) SqlMapConfig.xml에 각 기능별로 실행할 SQL문을 SqlMap.xml에 미리 작성한 후 등록한다.

(2) 애플리케이션에서 데이터베이스와 연동하는데 필요한 데이터를 각각의 매개변수에 저장한 후 마이바티스에 전달한다.

(3) 애플리케이션에서 요청한 SQL문을 SqlMap.xml에서 선택한다.

(4) 전달한 매개변수와 선택한 SQL문을 결합한다.

(5) 매개변수와 결합된 SQL문을 DBMS에서 실행한다.

(6) DBMS에서 반환된 데이터를 애플리케이션에서 제공하는 적당한 매개변수에 저장한 후 반환한다.

* 다시 정리하면 데이터베이스 연동 시 사용되는 SQL문을 미리 SqlMapConfig.xml에 작성해 놓고 애플리케이션에서 데이터베이스 연동 시 해당 SQL 문에서 사용될 데이터를 지원하는 해당 매개변수에 저장한 후 SQL문에 전달한다. 전달된 매개변수와 SQL문을 결합해 SQL문을 DBMS로 전송하여 실행한다. 그리고 그 결과를 애플리케이션에서 제공하는 자료형으로 반환한다.

* 이제 실습에 필요한 마이바티스를 다운로드하여 설치해보자.


2. 마이바티스 설치하기

1. http://www.mybatis.org 

 

mybatis

 

mybatis.org

에 접속한 후, Products 탭을 클릭한다.

2. MyBatis 3 옆에 있는 download 링크를 클릭한다.

3. mybatis-3.4.6.zip 파일을 클릭해 다운로드 한다.

4. 다운로드한 압축 파일의 압축을 해제한다.

5. mybatis-3.4.6.jar 를 복사해 새로 만든 프로젝트의 lib 폴더에 붙여 넣는다. 그리고 이 버전에 대응하는 ojdbc.jsr 파일도 복사해 붙여 넣는다. JSTL 관련 라이브러리도 이전 세팅을 참고해 붙여 넣는다.


3. 마이바티스 이용해 회원 기능 실습하기

* 이번에는 마이바티스를 이용해 회원 테이블과 연동하여 다양한 회원 관련 기능을 수행해 보자.


(1) 마이바티스 설정 파일 작성

* 마이바티스에서는 다음 표에 있는 두 가지 설정 파일을 사용한다.

< 마이바티스 관련 설정 파일 >

설정 파일 기능
SqlMapConfig.xml 데이터베이스 연동 시 반환되는 값을 저장할 빈이나 트랜잭션, 데이터소스 등 마이바티스 관련 정보를 설정한다.
member.xml 회원 정보 관련 SQL 문을 설정한다.

* 스프링처럼 마이바티스도 SqlMapConfig.xml 에서 데이터소스(DataSource)를 설정해 데이터베이스와 연동할 수 있는 기능을 제공한다. 그리고 실제 개발 시 마이바티스에서 제공하는 데이터소스를 사용해 데이터베이스와 연동한다.

1. 다음과 같이 설정 파일들을 추가한다. 이 때 각 설정 파일은 src 패키지 아래에 위치해야 한다는 것을 잊지 말자!

src 패키지 아래에 SqlMapConfig.xml은 mybatis 패키지에, member.xml은 /mybatis/mappers 패키지에 위치한다.

2. SqlMapConfig.xml을 다음과 같이 작성한다. <typeAlias> 태그는 애플리케이션에서 SQL문으로 값을 전달하거나 마이바티스에서 DBMS로 SQL문 실행 시 반환되는 레코드를 저장하는 용도로 사용하기 위한 빈인 memberVO 빈을 생성한다. 또한 <dataSource> 태그를 이용해 마이바티스가 연동하는 데이터베이스에 대한 데이터소스를 설정한다. 마지막으로 <mappers> 태그를 이용해 마이바티스에서 사용하는 SQL 문이 있는 XML 파일의 위치를 지정한다.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
	<typeAliases>
	   <typeAlias type="com.spring.ex01.MemberVO" alias="memberVO"/>
	   <!-- DAO에서 SQL 문으로 값을 전달할 때 또는 SQL 문을 실행한 후 가져온 값을 DAO로 전달할 때 사용할 빈을 생성한다. -->
	</typeAliases>

	<environments default="development">
	  <environment id="development">
	     <transactionManager type="JDBC"/>
	     <dataSource  type="POOLED">
	         <property name="driver" value="oracle.jdbc.driver.OracleDriver" />
	         <property  name="url" value="JDBC:oracle:thin:@localhost:1521:XE" />
	         <property name="username" value="scott" />
	         <property name="password" value="tiger"/>   
	     </dataSource> <!-- 데이터베이스 연결을 설정한다. -->
	  </environment>
	</environments>

	<mappers>
	   <mapper resource="mybatis/mappers/member.xml"/>
	   <!-- 회원 기능 관련 SQL문이 있는 member.xml을 읽어 들인다. 이 때 반드시 지정한 패키지 이름과 일치해야 한다는 것에 주의하자 -->
	</mappers>
</configuration>

3. member.xml을 다음과 같이 작성하여 회원 기능과 관련된 SQL문을 설정한다. 먼저 다른 파일의 SQL 문과 구별하기 위해 mapper.member 로 네임스페이스를 지정하고 SQL문을 실행한 후 반환되는 회원 정보 레코드를 저장하기 위한 resultMap을 지정한다. 각 레코드는 한 개의 MemverVO 객체를 생성한 후 지정한 속성에 따라 레코드의 컬럼 값을 저장한다. <select> 태그의 id 속성은 MemberDAO 에서 SQL 문을 구분해서 호출하는 용도로 사용되며, 반환되는 레코드를 id가 memResult 인 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="mapper.member"> <!-- member.xml의 네임스페이스를 지정한다. -->
	<resultMap id="memResult" type="memberVO"> <!-- SQL문을 실행한 후 반환되는 레코드들을 <typeAlias> 태그에서 지정한 memberVO 빈에 저장한다. -->
		<result property="id" column="id" />
        <result property="pwd" column="pwd" />
        <result property="name" column="name" />
        <result property="email" column="email" />
        <result property="joinDate" column="joinDate" /> <!-- 레코드의 컬럼 이름에 대해 memberVO의 같은 속성에 값을 설정한다. -->
	</resultMap> 
	
	<!-- DAO에서 id를 이용해 해당 SQL 문을 호출하고, 반환되는 레코드를 memResult에 저장한다.
	<resultMap id="memResult" type="java.util.HashMap">
      <result property="id" column="id" />
      <result property="pwd" column="pwd" />
      <result property="name" column="name" />
      <result property="email" column="email" />
      <result property="joinDate" column="joinDate" />
   </resultMap> -->


	<select id="selectAllMemberList" resultMap="memResult">
      <![CDATA[
         select * from t_member	order by joinDate desc	 	
      ]]>
	</select>

	<select id="selectName" resultType="String">
    <![CDATA[
	select name from t_member
	where id = 'hong'			
    ]]>
	</select>
	
	<select id="selectPwd" resultType="int" >
	  <![CDATA[ 
	    select pwd from t_member 
	    where id = 'hong'
	 ]]>
	 </select> 
	 
	<select id="selectMemberById" resultType="memberVO"  parameterType="String" >
      <![CDATA[
         select * from t_member
         where
         id=#{id}			
      ]]>
	 </select>	
	
	<select id="selectMemberByPwd" resultMap="memResult"  parameterType="int" >
      <![CDATA[
         select * from t_member
         where
         pwd = #{pwd}			
      ]]>
    </select>
    
     <insert id="insertMember"  parameterType="memberVO">
		<![CDATA[
		 insert into t_member(id,pwd, name, email)
		 values(#{id}, #{pwd}, #{name}, #{email})
		]]>      
	</insert>
	
	<insert id="insertMember2"  parameterType="java.util.Map">
		<![CDATA[
			 insert into t_member(id,pwd, name, email)
			 values(#{id}, #{pwd}, #{name}, #{email})
		]]>      
   </insert>
   
   <update id="updateMember"  parameterType="memberVO">
     <![CDATA[
	     update t_member
	     set pwd=#{pwd}, name=#{name}, email=#{email}
	     where
	     id=#{id}
      ]]>      
   </update> 
     
   <delete id="deleteMember"  parameterType="String">
	<![CDATA[
	   delete from  t_member
	   where
	   id=#{id}
	]]>      
  </delete>
	
	
	<!-- 동적 SQL문 -->
  <select id="searchMember" parameterType="memberVO" resultMap="memResult">
      <![CDATA[
          select * from t_member
      ]]>
      <where>
         <if test=" name != ''  and  name != null">
            name=#{name}
         </if>
         <if test="email != ''  and email != null ">
           and email = #{email}
         </if>
      </where>
      order by joinDate desc
  </select>
  
 <!--  
   <sql id="a">
    <![CDATA[
      select * from t_member
     ]]> 
   </sql>  -->  
  
<!--    
  <select id="searchMember" parameterType="memberVO" resultMap="memResult">
	     <include refid="a" /> 
	   <![CDATA[
         select * from t_member 
      ]]>
	  
       <where>
		  <choose>
		      <when test="name != '' and name != null and  email != '' and email != null">
			     name=#{name} and email=#{email}
		      </when>
		      <when test="name != '' and name != null">
			     name = #{name}
		      </when>
		      <when test="email !='' and email != null">
			    email = #{email}
		      </when>
	      </choose>
       </where>
       order by joinDate desc
   </select> -->
   
   <select id="foreachSelect" resultMap="memResult" parameterType="java.util.Map">
      <!-- <include refid="a" /> -->
      <![CDATA[
        select * from t_member 
          
      ]]>
      
      where name in
      <foreach item="item" collection="list" open="(" separator="," close=")" >
         #{item}
      </foreach>
      order by joinDate desc
   </select>
   
   <insert id="foreachInsert"  parameterType="java.util.Map">
      <foreach item="item"  collection="list"   open="INSERT ALL" separator=" " close="SELECT * FROM DUAL" >
          INTO  t_member(id, pwd, name, email)
          VALUES  (#{item.id},
                    #{item.pwd},
                    #{item.name},
                    #{item.email})
      </foreach>
   </insert>
   
   <!-- 
    <select id="selectLike" resultMap="memResult"  parameterType="String" >
      <![CDATA[
         select * from t_member
         where
         name like '%'#{name}'%'		
      ]]>
    </select>
     -->
   <!--  like 검색 -->
   <select id="selectLike" resultMap="memResult"  parameterType="String" >
      <![CDATA[
         select * from t_member
         where
         name like '%' || #{name} || '%'		
      ]]>
    </select>
</mapper>

(2) 마이바티스를 이용한 회원 정보 조회 실습

* 그럼 본격적으로 마이바티스를 이용해 회원 정보를 조회해 보자.

1. 실제 마이바티스와 연동하기 위한 자바 클래스와 JSP 파일을 다음과 같이 준비한다.

2. MemberServlet을 다음과 같이 작성한다. 브라우저에서 요청 시 MemberDAO 객체를 생성한 후 selectAllMemberList()를 호출하는 서블릿이다.

package com.spring.ex01;

import java.io.IOException;
import java.util.List;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/mem.do")
public class MemberServlet extends HttpServlet {
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doHandle(request, response);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doHandle(request, response);
	}

	private void doHandle(HttpServletRequest request, HttpServletResponse response)	throws ServletException, IOException {
		request.setCharacterEncoding("utf-8");
		response.setContentType("text/html;charset=utf-8");
		MemberDAO dao = new MemberDAO();
		List<MemberVO> membersList = dao.selectAllMemberList(); // MemberDAO 객체를 생성하고 selectAllMemberList() 를 호출한다.
		//List<HashMap<String, String>> membersList = dao.selectAllMemberList();
		request.setAttribute("membersList", membersList);
		RequestDispatcher dispatch = request.getRequestDispatcher("test01/listMembers.jsp");
		dispatch.forward(request, response);
	}
}

3. MemberDAO 클래스를 다음과 같이 작성한다. SqlMapConfig.xml 파일을 이용해 SqlMapper 객체를 생성한다. 그런 다음 selectAllMemberList() 메서드를 호출하면서 인자로 mapper.member.selectAllMemberList를 전달해 member.xml에서 해당 네임스페이스와 id에 해당하는 SQL문을 작성한다.

package com.spring.ex01;

import java.io.Reader;
import java.util.List;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class MemberDAO {
	public static SqlSessionFactory sqlMapper = null;

	private static SqlSessionFactory getInstance() {
		if (sqlMapper == null) {
			try {
				String resource = "mybatis/SqlMapConfig.xml"; // MemberDAO의 각 메서드 호출 시 src/mybatis/SqlMapConfig.xml에서 설정 정보를 읽은 후 데이터베이스와의 연동 준비를 한다.
				Reader reader = Resources.getResourceAsReader(resource);
				sqlMapper = new SqlSessionFactoryBuilder().build(reader); // 마이바티스를 이용하는 sqlMapper 객체를 가져온다.
				reader.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return sqlMapper;

	}

	public List<MemberVO> selectAllMemberList() {
		sqlMapper = getInstance();
		SqlSession session = sqlMapper.openSession(); // 실제 member.xml의 SQL문을 호출하는 데 사용되는 SqlSession 객체를 가져온다.
		List<MemberVO> memlist = null;
		memlist = session.selectList("mapper.member.selectAllMemberList"); // 여러 개의 레코드를 조회하므로 selectList() 메서드에 실행하고자 하는 SQL문의 id를 인자로 전달한다.
		return memlist;
	}
	
//	 public List<HashMap<String, String>> selectAllMemberList() { 
//		sqlMapper = getInstance(); 
//     	SqlSession session = sqlMapper.openSession();
//		List<HashMap<String, String>> memlist = null; 
//   	memlist = session.selectList("mapper.member.selectAllMemberList"); 
//		return memlist; 
//	 }
	
}

4. MemberVO 클래스를 다음과 같이 작성한다. SQL문으로 전달할 값이나 SQL문을 실행한 후 반환되는 레코드들의 값을 각 속성에 저장한다.

package com.spring.ex01;

import java.sql.Date;

public class MemberVO {
	private String id;
	private String pwd;
	private String name;
	private String email;
	private Date joinDate;

	public MemberVO() {}
	public MemberVO(String id, String pwd, String name, String email) {
		this.id = id;
		this.pwd = pwd;
		this.name = name;
		this.email = email;
	}

	public String getId() {
		return id;
	}

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

	public String getPwd() {
		return pwd;
	}

	public void setPwd(String pwd) {
		this.pwd = pwd;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public Date getJoinDate() {
		return joinDate;
	}

	public void setJoinDate(Date joinDate) {
		this.joinDate = joinDate;
	}

}

5. 회원 정보를 표시하는 listMambers.jsp는 이전의 listMembers.jsp를 복사해서 사용하자.

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" 
    isELIgnored="false"  %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"  %>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />

<%
  request.setCharacterEncoding("UTF-8");
%>

<!DOCTYPE html>
<html lang="ko">

    <head>
        <meta charset="UTF-8" />
        <title>회원 정보 출력창</title>
    </head>

    <body>
        <table border="1" align="center" width="80%">
            <tr align="center" bgcolor="lightgreen">
                <td><b>아이디</b></td>
                <td><b>비밀번호</b></td>
                <td><b>이름</b></td>
                <td><b>이메일</b></td>
                <td><b>가입일</b></td>
            </tr>

            <c:forEach var="member" items="${membersList}">
                <tr align="center">
                    <td>${member.id}</td>
                    <td>${member.pwd}</td>
                    <td>${member.name}</td>
                    <td>${member.email}</td>
                    <td>${member.joinDate}</td>

                </tr>
            </c:forEach>
        </table>
        <a href="${contextPath}/member/memberForm.do">
            <h1 style="text-align:center">회원가입</h1>
        </a>
    </body>

</html>

6. 다음의 주소로 요청하여 실행 결과를 확인한다.

- http://localhost:8090/pro23/mem.do