23장. 마이바티스 프레임워크 사용하기

23.1 마이바티스란?

  • 애플리케이션 규모가 커지면서 JDBC로 개발하는데 한계(SQL문이 복잡, 연동 작업이 반복됨)가 생기며 마이바티스나 하이버네이트 같은 데이터 연동 관련 프레임워크가 등장
  • 마이바티스를 사용하면 복잡한 SQL문도 SQL Developer 같은 도구에서 SQL문을 사용하는 것처럼 표준화된 방법으로 사용 가능

 

<마이바티스 프레임워크의 특징>

  • SQL 실행 결과를 자바 빈즈나 Map 객체에 매핑해주는 Persistence 솔루션으로 관리

     => SQL을 소스코드가 아닌 XML로 분리

  • SQL문과 프로그래밍 코드를 분리해서 구현
  • datasource 기능과 트랜잭션 처리 기능을 제공

 

<마이바티스 프레임워크 동작 과정>

  1. 각 기능별로 실행할 SQL문을 xml파일(이하 SqlMap.xml이라 칭함)에 작성한 후 SqlMapConfig.xml에 등록
  2. 실행하고 싶은 sql문의 id와 데이터를 매개변수에 저장한 후 마이바티스에 전달
  3. 애플리케이션에서 요청한 SQL문을 SqlMap.xml에서 선택
  4. 전달한 매개변수와 선택한 SQL문을 DBMS에서 실행
  5. DBMS에서 반환된 데이터를 애플리케이션에서 제공하는 적당한 매개변수에 저장한 후 반환

 

 

23.2 마이바티스 설치하기

www.mybatis.org에 접속한 후 Products 탭 > MyBatis3 download > mybatis-x.x.x.zip 다운 >  mybatis-x.x.x.jar 파일을 lib폴더에 넣기

+) ojdbc6.jar과 JSTL 라이브러리도 넣기

 

 

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

* SqlMapConfig.xml : db 연동 시 반환되는 값을 저장할 빈이나 트랜잭션, 데이터소스 등 마이바티스 관련 정보를 설정

* member.xml : 회원 정보 관련 SQL문을 설정

>> 두 파일 모두 src 패키지 아래에 위치

 

- SqlMapConfig.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"/>
	</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="C##scott"/>
				<property name="password" value="tiger"/>
			</dataSource>
		</environment>
	</environments>
	
	<mappers>
		<mapper resource="mybatis/mappers/member.xml"/>
	</mappers>
</configuration>java

>> typeAlias : DAO에서 SQL문으로 값을 전달할 때 또는 SQL문을 실행한 결과 값을 DAO로 전달할 때 사용할 빈 생성

>> dataSource : 마이바티스 연동하는 db에 대한 데이터소스를 설정

>> mapper : 마이바티스에서 사용하는 SQL문이 있는 xml파일의 위치를 지정해서 읽어드림

 

- member.xml

<?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">
	<resultMap id="memResult" type="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"/>
	</resultMap>
	
	<select id="selectAllMemberList" resultMap="memResult">
		<![CDATA[select * from t_member order by joinDate desc]]>
	</select>
</mapper>html

>> namespace : 다른 파일의 SQL문과 구분하기 위해 지정

>> resultMap : SQL문을 실행한 후 반환되는 레코드를 저장하기 위해 지정(typeAlias 태그에서 지정한 빈임)

     >> type : 레코드의 컬럼 값을 속성에 저장하고 객체를 생성

>> select id : dao에서 SQL문을 구분하여 호출하기 위함

     select resultMap : 위에서 지정한 resultMap의 id

>> SQL문의 < > <= >= 같은 연산자를 XML파일에서는 특수문자로 인식하여 오류 발생, 이러한 연산자들도 SQL문의 일부라는 것을 알려주기 위해 <![CDATA[...]> 태그 안에 SQL문을 작성

 

- MemberServlet.java

...
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 membersList=dao.selectAllMemberList();
		
		request.setAttribute("membersList", membersList);
		RequestDispatcher dispatch=request.getRequestDispatcher("test01/listMembers.jsp");
		dispatch.forward(request, response);
	}java

 

- MemberDAO.java

public class MemberDAO {
	
	private static SqlSessionFactory sqlMapper=null;
	
	public static SqlSessionFactory getInstance() {
		if(sqlMapper==null) {
			try {
				String resource="mybatis/SqlMapConfig.xml";
				Reader reader=Resources.getResourceAsReader(resource);
				sqlMapper=new SqlSessionFactoryBuilder().build(reader);
				reader.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return sqlMapper;
	}

	public List selectAllMemberList() {
		sqlMapper=getInstance();
		SqlSession session=sqlMapper.openSession();
		
		List<MemberVO> memList=null;
		memList=session.selectList("mapper.member.selectAllMemberList");
		return memList;
	}

}java

>> 각 메서드 호출 시 src/mybatis/SqlMapConfig.xml에서 설정 정보를 읽은 후 db와의 연동 준비

>> ~Builder : 마이바티스를 이용하는 sqlMapper 객체를 가져옴

>> openSession() : member.xml의 SQL문을 호출하는데 사용되는 SqlSession 객체를 가져 옴

>> selectList : 실행하고자 하는 SQL문의 id를 인자로 전달하여 여러 개의 레코드를 조회

 

 

23.4 마이바티스 이용해 회원 정보 CRUD 실습하기

 

<SqlSession 클래스에서 제공하는 여러 가지 메서드>

메소드 기능
List selectList(query_id, 조건) id에 대한 select 문을 실행한 후 여러 레코드를 List로 반환 / 조건(파라미터)는 선택
T selectOne(query_id) id에 대한 select 문을 실행한 후 지정된 타입으로 한 개의 레코드 반환 / 조건(파라미터)는 선택
Map<K,V> selectMap(query_id, 조건) id에 대한 select문을 실행하면서 사용되는 조건도 전달. Map타입으로 레코드 반환
int insert(query_id, Object obj) id에 대한 insert문을 실행하면서 객체 obj 값을 테이블에 추가
(sql문에서 #{}로 바로 obj의 속성 값 접근가능
int update(query_id, Object obj) obj 객체의 값을 조건문의 수정 값으로 사용해 id데 대한 update문 실행
int delete(query_id, Object obj) obj 객체의 값을 조건문의 조건 값으로 사용해 id데 대한 delete문 실행

 

 

<조건 값으로 회원 ID/pwd 조회 실습>

방법1) VO 이용

-servlet

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();
    MemberVO memberVO=new MemberVO();

    String name=dao.selectName();
    int pwd=dao.selectPwd();

    PrintWriter out=response.getWriter();
    out.write("<script> alert('이름 : "+name+" 비밀번호: "+pwd+"'); </script>");

    String nextPage="";
    String action=request.getParameter("action");
    if(action==null || action.contentEquals("listMembers")) {
        List<MemberVO> membersList=dao.selectAllMemberList();
        request.setAttribute("membersList", membersList);
        nextPage="test01/listMembers.jsp";
    }
    else if(action.contentEquals("selectMemberById")) {
        String id=request.getParameter("value");
        memberVO=dao.selectMemberById(id);
        request.setAttribute("mem", memberVO);
        nextPage="test01/memberInfo.jsp";
    }
    else if(action.contentEquals("selectMemberByPwd")) {
        String pwd2=request.getParameter("value");
        List<MemberVO> membersList=dao.selectMemberByPwd(pwd2);
        request.setAttribute("membersList", membersList);
        nextPage="test01/listMembers.jsp";
    }

    RequestDispatcher dispatcher=request.getRequestDispatcher(nextPage);
    dispatcher.forward(request, response);
}java

- dao

...
public String selectName() {
    sqlMapper=getInstance();
    SqlSession session=sqlMapper.openSession();

    String name=session.selectOne("mapper.member.selectName");

    return name;
}

public int selectPwd() {
    sqlMapper=getInstance();
    SqlSession session=sqlMapper.openSession();

    return session.selectOne("mapper.member.selectPwd");
}

public MemberVO selectMemberById(String id) {
    sqlMapper=getInstance();
    SqlSession session=sqlMapper.openSession();

    return session.selectOne("mapper.member.selectMemberById",id);

}

public List selectMemberByPwd(String pwd) {
    sqlMapper=getInstance();
    SqlSession session=sqlMapper.openSession();

    List<MemberVO> membersList=session.selectList("mapper.member.selectMemberByPwd",pwd);

    return membersList;
}java

>> selectOne / selectList의 두번째 인자를 넘겨주면 member.xml에서 ${id} 처럼 바로 조건 값으로 쓸 수 있음

 

- member.xml

<mapper namespace="mapper.member">
 	<resultMap id="memResult" type="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"/>
	</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="String">
		<![CDATA[select * from t_member where pwd=#{pwd}]]>
	</select>
	
</mapper>html

>> parameterType : 조건값의 데이터 타입을 지정

>> 전달된 조건 값은 #{값}의 형식으로 사용

>> resultType="memberVO" : 조회되는 레코드를 반환하는데 그 개수가 한개인 경우

@@ pwd가 db에 string형으로 저장되어 있기 때문에 parameterType도 string이어야한다. int가 될 수 없다.

 

 

방법2) HashMap 이용

- servlet, dao

//servlet
..
MemberDAO dao=new MemberDAO();
List<HashMap<String,String>> membersList=dao.selectAllMemberList();
...



//dao
..
List<HashMap<String,String>> memList=null;
memList=session.selectList("mapper.member.selectAllMemberList");
..java

- member.xml

	...
    <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>
    ...html

>> resultMap의 type을 HashMap으로, resultMap의 VO가 필요없음

>> key-컬럼이름 , value-값

 

 

<회원 정보 추가>

>> member.xml에서 insert태그 사용

>> 조건으로 vo나 hashmap을 넘겨주면 속성/키를 #{}로 자유롭게 사용 가능

>> session.insert !

>> session.commit() 필요

 

방법1) VO 이용

- memberForm.jsp

<form method="post"   action="${contextPath}/mem4.do?action=insertMember">
<h1  class="text_center">회원 가입창</h1>
<table  align="center">
   <tr>
      <td width="200"><p align="right">아이디</td>
      <td width="400"><input type="text" name="id"></td>
   </tr>
   ...html

- servlet

...
else if(action.contentEquals("insertMember")) {
    String id=request.getParameter("id");
    String pwd=request.getParameter("pwd");
    String name=request.getParameter("name");
    String email=request.getParameter("email");

    memberVO=new MemberVO(id,pwd,name,email);
    dao.insertMember(memberVO);
    nextPage="/mem4.do?action=listMembers";
}
...java

- dao

public int insertMember(MemberVO memberVO) {
    sqlMapper=getInstance();
    SqlSession session=sqlMapper.openSession();

    int result=0;
    result=session.insert("mapper.member.insertMember",memberVO);
    session.commit();

    return result;
}java

- member.xml

<insert id="insertMember" parameterType="memberVO">
    <![CDATA[insert into t_member(id,pwd,name,email) values(#{id},#{pwd},#{name},#{email})]]>
</insert>html

 

방법2) HashMap 이용

- servlet / member.xml

//servlet
..
Map memberMap=new HashMap();
memberMap.put("id",id);
memberMap.put("pwd", pwd);
memberMap.put("name", name);
memberMap.put("email", email);
..


//member.xml
<insert id="insertMember2" parameterType="java.util.HashMap">
    <![CDATA[insert into t_member(id,pwd,name,email) values(#{id},#{pwd},#{name},#{email})]]>
</insert>java

 

 

<회원 정보 수정 실습>

>> 추가와 member.xml만 다르고 나머진 거의 같음~ (여기선 회원가입과 동일하게 작성)

- dao

result=session.update("mapper.member.updateMember",memberVO);
session.commit();java

-member.xml

<update id="updateMember" parameterType="MemberVO">
    <![CDATA[update t_member set pwd=#{pwd}, name=#{name}, email=#{email} where id=#{id}]]>
</update>html

 

<회원 정보 삭제 실습>

>> 이 역시 member.xml만 다름

- list.jsp

..
 <td><b>삭제</b></td>
</tr>
<c:forEach var="mem" items="${membersList }">
<td><a href="${contextPath}/mem4.do?action=deleteMember&id=${member.id }">삭제하기</a></td>
..html

- dao

result =session.delete("mapper.member.deleteMember",id);
session.commit();java

- member.xml

<delete id="deleteMember" parameterType="String">
    <![CDATA[delete from t_member where id=#{id}]]>
</delete>html

 

 

 

23.5 마이바티스의 동적 SQL문 사용하기

  • (기존 SQL문) 각각의 조건절에 따라 각각의 SQL문을 작성해야 함
  • 마이바티스의 동적 SQL문 : if. foreach 등을 이용해 한개의 SQL문으로 각 조건절들을 구현
  • 동적 SQL문은 주로 where절을 동적으로 추가
  • 동적 SQL문에 사용되는 태그들은 JSP의 JSTL에서 사용되는 코어(c) 태그들과 유사
  • <![CDATA.. 안에 쓰지 않아도 됨

 

<동적 SQL문을 구성하는 요소들>

  • if
  • choose(when, otherwise)
  • trim(where, set)
  • foreach

 

<if> 태그로 동적 SQL문 만들기

  • <where> 태그 안에서 사용(where 조건 절에서 where 표시)
  • <where> 태그 : <if> 태그에 따라 조건식이 존재하면 공통 SQL문에 where절을 추가
  • <where> <if test="조건식"> 추가할 구문 </if> </where>

 

- members.xml

<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>html

>>test 조건식에서 name, email은 memberVO의 속성이다.

>> 조건절에 속성 사이에 and를 써야 함에 주의!!

 

- servlet

else if(action.contentEquals("searchMember")) {
    String name=request.getParameter("name");
    String email=request.getParameter("email");
    memberVO.setName(name);
    memberVO.setEmail(email);

    List<MemberVO> members=dao.selectMember(memberVO);
    request.setAttribute("membersList", members);
    nextPage="test03/listMembers.jsp";
}java

 

- dao

public List<MemberVO> selectMember(MemberVO memberVO) {
    sqlMapper=getInstance();
    SqlSession session=sqlMapper.openSession();

    List<MemberVO> list=session.selectList("mapper.member.searchMember", memberVO);

    return list;
}java

 

 

<choose> 태그로 동적 SQL문 만들기

- members.xml

<select id="searchMember" parameterType="MemberVO" resultMap="memResult">
    <![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">
                and email=#{email}
            </when>
        <when>
    </where>
    order by joinDate desc
</select>html

 

>> switch문과 같은 기능으로, 위에서부터 맞는 조건이 있으면 빠져 나옴

>> <choose> <when test=조건식> ~ </when> <choose>

>> otherwise문도 당연히 쓸 수 있음

 

 

<foreach> 태그로 회원 정보 조회하기

<foreach item="item" collection="list" index="index" open=" (" close=")" separator=","> #{item} </foreach>

  • collection : 전달받은 인자 값의 데이터 타입, 유형은 list나 array 둘 중 하나. => parameterType은 java.util.Map이다.
  • index : 몇번 째 반복인지를 담은 변수, 최초는 0
  • open / close : 구문이 시작/끝날 때 지정한 기호 추가
  • separator : 반복되는 사이에 추가되는 기호

 

~ where name in (a.b,c) 를 만들어보자

 

- member.xml

<select id="foreachSelect" parameterType="java.util.Map" resultMap="memResult">
		<![CDATA[select * from t_member]]>
		where name in
		<foreach item="item" collection="list" open=" (" close=")" separator=",">
			#{item}
		</foreach>
		order by joinDate desc
</select>html

>> parametrType의 요소를 item으로 지칭

 

- servlet

else if(action.contentEquals("foreachSelect")){
			List<String> nameList=new ArrayList();
			nameList.add("차범근");
			nameList.add("이순신");
			nameList.add("홍길동");
			
			List<MemberVO> membersList=dao.foreachSelect(nameList);
			request.setAttribute("membersList", membersList);
			nextPage="test03/listMembers.jsp";
		}java

 

- dao

public List<MemberVO> foreachSelect(List<String> nameList) {
    sqlMapper=getInstance();
    SqlSession session=sqlMapper.openSession();

    return session.selectList("mapper.member.foreachSelect",nameList);
}java

 

<foreach> 태그로 bulkInsert

  •  오라클에서는 insert문을 동시에 여러 개 사용하면 오류 발생(MySQL은 ㄱㅊ)
  • 해결방법!!>> <foreach> 태그의 open과 close 속성에 SQL문을 설정한 후 서브 쿼리 형식으로 다중 insert문 구현
  • INSERT ALL INTO t_member VALUES()'(반복) SELECT * FROM DUAL

- member.xml

<insert id="foreachInsert" parameterType="java.util.Map">
    <foreach item="item" collection="list" open="INSERT ALL" close="SELECT * FROM DUAL" separator=" ">
        INTO t_member(id,pwd,name,email)
        VALUES (#{item.id}, #{item.pwd}, #{item.name}, #{item.email})
    </foreach>
</insert>html

 

- servlet

else if(action.contentEquals("foreachInsert")) {
    List<MemberVO> memList=new ArrayList();
    memList.add(new MemberVO("m1","1234","박길동","m1@test.com"));
    memList.add(new MemberVO("m2","1234","이길동","m2@test.com"));
    memList.add(new MemberVO("m3","1234","유길동","m3@test.com"));

    int result=dao.foreachInsert(memList);
    nextPage="/mem4.do?action=listMembers";
}java

 

- dao

public int foreachInsert(List<MemberVO> memList) {
    sqlMapper=getInstance();
    SqlSession session=sqlMapper.openSession();

    int result=session.insert("mapper.member.foreachInsert",memList);
    session.commit();

    return result;
}java

 

 

<sql> 태그와 <include> 태그로 SQL문 중복 제거하기

  • <sql> 태그로 공통으로 들어가는 sql문을 지정할 수 있다. id 속성으로 이름을 지정
  • <include> 태그의 refid 속성에 위에서 지정한 sql문 이름을 넣어 공통 SQL문을 끼워 넣음

- member.xml

<sql id='a'>
	<![CDATA[select * from t_member ]]>
</sql>

<select id="searchMember" parameterType="MemberVO" resultMap="memResult">
    <include refid='a'/>
    <where>
    ...html

 

 

* 마이바티스에서 오라클 연동해 like 검색하는 방법

where name like '%'|| #{속성} || '%'

처럼 '%' 기호와 조건 값 사이에는 || 기호를 넣어 연결

예시) select * from t_member where name like '%' || #{name} || '%'

 

- member.xml

	<select id="selectLike" parameterType="String" resultMap="memResult">
		<include refid="a"/>
		<![CDATA[where name like '%' || #{name} || '%']]>
	</select>html

 

- servlet

else if(action.contentEquals("selectLike")) {
    String name="길";
    List<MemberVO> membersList=dao.selectLike(name);

    request.setAttribute("membersList", membersList);
    nextPage="test03/listMembers.jsp";
}java

 

- dao

public List<MemberVO> selectLike(String name) {
    sqlMapper=getInstance();
    SqlSession session=sqlMapper.openSession();

    return session.selectList("mapper.member.selectLike",name);
}java