본문 바로가기

자바 웹을 다루는 기술

[Mybatis 설치, 설정 xml 파일] 23장 xml 파일에서의 동적쿼리문

23장

 

ORM 으로 sQL안쓰고 객체 지향 데이터베이스를 쓰는데 장비가 좋아야 쓸수 있음 미국 네카라쿠배당토 이런데 만 씀

 

우리나라에서는 sql쓰고 유지보수가 쉬워서 MyBatis쓴다.

 

MyBatis

소스코드와 쿼리를 분리한다. XML파일로 빼서 관리, 코드가 복잡하면 논리적인 오류가 발생할 가능성이 높아지기 때문이다.

 

SQL문과 자바코드가 섞여서 불편하다 -> pstmt로 ? - > MyBatis로 쿼리 구성

 

sql 구문 실행

CRUD

insert Delete update   --> 정수리턴

select -> 필드/ 값 쌍으로 나옴

select의 결과가 객체로 Bean으로 나올수도있고 Map<String , Object>으로 나올수도있다

 

현업에서는 DataBase 연동은 무조건 DataSource로 해서 DBCP에서 연결 객체를 집어와야한다. -> 모르면 바보돼

 

XML파일에는 id : sql구문을 1 : 1 로관리한다. 파일이 여러개이면 폴더에 관련 사항별로 정리함

id는 보통 함수명과 일치한다.

insert Delete update는 입력이 bean Map 이 들어가서 return 실행된 행의 값 이 나온다.

select는 입력자료를 Bean이나 Map이 나오고 출력 결과가 Bean이나 Map이 된다.

 

Mybatis

지금은 다운 받지만 나중에 메이븐이나그래들을 통해서 자동으로 추가가능해지니다.

 

https://blog.mybatis.org/

 

The MyBatis Blog

A blog about the the MyBatis data mapper framework.

blog.mybatis.org

 

 

여기에는 source code가 있는데 기능추가하고 구조틀 공부하려면 1버전대의 아주 최하위 버전부터 공부해보는거 나쁘지 않음 그뒤의기능추가부분ㅇ만추가 공부하면 되서

마이바티스 설명 문서 까지 갖고 있어야함 한글 해석본 찾아보기

 

 

마이바티스를 사용하려면 SqlMapConfig.xml 설정파일을 작성해줘야한다. DataSource를 설정해 연동해야한다.

 

자바 17버전으로 해보겠음

웹프로젝트 lib밑에 mybatis.jar를 추가한다.

 

SqlMapConfig.xml

 

alias는 sql을 저장한 xml에 resultType에 사용할수잇다. 최상위루트노드에 configuration이 있고 그아래에  <typeAliases> DTO(VO)로 전달 받는것에 대한 타입설정을 해준다.  <environments>안쪽에는 database연결설정에 관한것을 작성해준다. <mappers>에는 sql문이 저장되어있는 파일을 등록해준다. 그러면 namespace에 저장된 키를 갖고 실행해야하는 sql문 id까지 합쳐서 sql을 찾아다가 실제 dB에 전달해서 컴파일해준다.

<?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>
   <!-- DTO에 대한타입설정 bean을 받아서 처리할꺼니까 -->
      <typeAlias type="com.spring.ex01.MemberVO" alias="memberVO"/>
   </typeAliases>
<!-- 환경설정 -->
   <environments default="development">
     <environment id="development">
        <transactionManager type="JDBC"/>
        <!-- server context.xml에서 했던거 여기서는 이렇게 -->
        <dataSource  type="POOLED">
        <!-- oracleDriver설정 -->
            <property name="driver"  value="oracle.jdbc.driver.OracleDriver" />
            <property  name="url"    value="JDBC:oracle:thin:@localhost:1521:XE" />
            <property name="username" value="kosa" />
            <property name="password"  value="1004"/>      
            <!-- 이제 연결과 닫기 그리고 결과값구성은 mybatis가 관리 -->  
        </dataSource>
     </environment>
   </environments>

<mappers>
<!-- 파일설정 폴더 매핑해준다. 여기꺼를 DATABASE에 연결해서 sql을 컴파일 할꺼예요 -->
   <mapper resource="mybatis/mappers/member.xml"/>
</mappers>
</configuration>

 

 

member.xml

<mapper>에는 namespace에 해당하는 이 문서의 별칭을 설정해주고  이 태그 사이에 결과값을 어떤식으로 받아올것인지를 설정하는 resultMap을 설정해준다. 여기서 Bean으로 받아올 것인지 map으로 받아올 것인지에 따라 <select>안의 쿼리문에 대한 결과 자료형이 달라진다. 

매핑안해줫을때는 데이터베이스의 컬럼명 그대로 대문자로 받아오니 주의 하자

<?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">
<!-- 결과 매핑 DB의 이름과 자바 Bean객체와의 이름,자료형이 다를때 사용한다.(같을때는 사용할 필요없다.)(자동형변환해줌)-->
<!-- 일종의 자료형 memResult, SqlMapConfing에 설정해준 타입 -->
<!-- bean방식으로 넘겨받기 -->
<!-- 	<resultMap id="memResult" type="memberVO">
	column이 데이터베이스에 속성값과 일치해야한다
		<result property="id" column="id" />
        <result property="pwd" column="pwd" />
        <result property="uname" column="uname" />
        <result property="birth" column="birth" />
        <result property="gender" column="gender" />
        <result property="phone" column="phone" />
	</resultMap>   -->
<!-- Map방식으로 넘겨받기-->
	<resultMap id="memResult" type="java.util.HashMap">
	<!-- 데이터 베이스 걸 그대로 갖다 쓰기때문에 출력할때 ${}에서 컬럼명을 대문자로 출력해야함 -->
      <result property="id" column="id" />
      <result property="pwd" column="pwd" />
       <result property="uname" column="uname" />
       <result property="birth" column="birth" />
       <result property="gender" column="gender" />
       <result property="phone" column="phone" /> 
   </resultMap> 

<!-- namespace + select id여야지 실제 ID 가 된다., resulpMap:리턴타입 -->
<!-- 이름과 자료형이 같으명 따로 선언해서 쓸필요가 없겠지 그러면 resultType="memberVO' -->
	<select id="selectAllMemberList" resultMap="memResult">
	<!-- xml안에서 <>관한 부분때문에 쓴것이다. <>없이 쓰면 에러여서 순수데이터임을 강조하기 위해서 사용한다. 안써도된다. -->
      <![CDATA[
         select * from member	order by id desc	 	
      ]]>
	</select>

</mapper>

 

 

$는 sqlInjection에 약해서 사용안함, #{}는 값을 형태만 맞으면 대치함ㅋ

--> 매개변수가 하나일때는 #{}으로 아무말이나써줘도 대입되는데 매개변수가 하나의 객체이면 그 객체안에 선언된 변수만 써줘야한다.xml 에서 써준sql문은 호출하지 않아도 일단 없는객체를 써주면 안된다. 호출하지않아도 구문이 틀리면 오류남

 

 

MemberDAO.java

java에서 xml 쿼리를 실행시키기 위해 sqlSesison에서 제공하는 여러가지 메서드

메서드 기능
List selectList(sql) select문을 List로 반환
List selectList(sql, 조건) 조건이있는
T selectOne(sql) DTO하나만 반환 하는 select문
T selectOne(sql, 조건) 조건이 있는
Map<K,V> selectMap(sql, 조건) Map으로 반환하는 select문
int insert(sql, DTO) 삽입할 DTO를 매개변수로 insert를 수행한다.
int update(sql ,DTO) 업데이트할 DTO를 매개변수로 update를 수행한다.
int delete(sql, DTO) 삭제할 DTO를 매개변수로 delete를 수행한다.

insert,update,delete는 내부적으로 executeUpdate써줘서 뭘써도 상관없다 insert할때 update써도 된다는말

mybatis는 autocommit이 아니여서 session.commit()해줘야함

 public int insertMember(MemberVO memberVO) { 
     sqlMapper = getInstance(); 
     SqlSession session = sqlMapper.openSession();
     int result  = session.insert("mapper.member.insertMember",memberVO);
     session.commit();
     return result;
 }

 

member.xml

<mapper namespace="mapper.member">
<select id="selectName" resultType="String">
	select uname from member
	where id = 'kim'			
	</select>
	
	<select id="selectPwd" resultType="String" >
	    select pwd from member 
	    where id = 'kim'
	 </select> 
	 
	<select id="selectMemberById" resultType="memberVO"  parameterType="String" >
         select * from member
         where
         id=#{id}			
	 </select>	
	
	<select id="selectMemberByPwd" resultMap="memResult"  parameterType="int" >
         select * from t_member
         where
         pwd = #{pwd}			
    </select>
    
     <insert id="insertMember"  parameterType="memberVO">
		 insert into member(id,pwd, uname, birth, gender,phone)
		 values(#{id}, #{pwd}, #{uname}, #{birth}, #{gender}, #{phone})
	</insert>
    <update id="updateMember"  parameterType="memberVO">
	     update member
	     set pwd=#{pwd}, uname=#{uname}, birth=#{birth}
	     where
	     id=#{id}
   </update> 
     
   <delete id="deleteMember"  parameterType="String">
	   delete from  member
	   where
	   id=#{id}
  </delete>
  </mapper>

 

<mybatis에서 구현하는 동적쿼리문>

 

내부적으로 preparestatement갖고 만듬

mybatis에서는 where 절의 시작이 and이면 생략해서 sql을 읽어와준다.

where절은 <where>을 통해 구성해준다. test속성에서 name 값이 들어오거나 null이 아니면  name이 일치하는 값을 찾아준다. text속성에서 문자 and 로 비교해준다.

	<!-- 동적 SQL문 -->
  <select id="searchMember" parameterType="memberVO" resultMap="memResult">
          select * from member
      <where>
         <if test=" uname != ''  and  uname != null">
            uname=#{uname}
         </if>
         <if test="phone != ''  and phone != null ">
           and phone like concat(concat('%',#{phone}),'%')
         </if>
      </where>
      order by id desc
  </select>

 

https://atoz-develop.tistory.com/entry/MyBatis-%EB%8F%99%EC%A0%81-SQL-choose%EC%99%80-set%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-%EB%8F%99%EC%A0%81-SQL-%EB%A7%8C%EB%93%A4%EA%B8%B0

 

MyBatis 동적 SQL - <choose>와 <set>을 사용하여 동적 SQL 만들기

관련 글 - [JAVA/WEB] 웹 프로젝트에 MyBatis 세팅 및 적용하기 - MyBatis 설정 파일 - SQL Mapper 작성 방법 - MyBatis 설정 파일 작성 방법 - MyBatis와 Log4J 연동하기 제목 MyBatis의 동적 SQL 기능을 사용하면 하나

atoz-develop.tistory.com

<foreach>

속성 설명
collection 배열과 list계열 인스턴스 사용가능
index 1씩 증가하면서 접근하는 값의 위치 나타낸다 초기값 0
item collection의 값을 차례로 하나씩 가져온다.
open 구문 시작시 지정한 기호를 추가
close 구문이 끝날때 지정한 기호를 추가
separator 반복되는 item 사이에 지정한 기호를 추가한다.

 

<in 검색>

mybatis에서 in으로 검색할때 foreach를사용하는데 이때 아이템은 문자이기때문에 문자열은 자동으로 ' '가 들어간다

여기서의 forEach문은 전달받는 애가 배열과 list계열만 된다.

   <select id="foreachSelect" resultMap="memResult" parameterType="java.util.Map">
      <!-- <include refid="a" /> -->
        select * from member 
      where uname in
      <foreach item="item" collection="list" open="(" separator="," close=")" >
         #{item}
      </foreach>
      order by id desc
   </select>

<여러개를 insert>

sql구문에서 Insert ALL이라는 여러개의 insert문을 한번에 실행해주는 구문이 있다. foreach를 통해서 시작 문자 반복 후 끝문자를 지정해 줄수있다. 이렇게쓰면 여러개의 insert문을 한번에 처리해줄수 있어 효율적이다. 근데 그냥 insert문써주는거랑 많이 차이나진 않긴함

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

<choose>

switch,if~elseif같은 애이다 위에 when에서 하나라도 일치하는 값이 있다면 아래는 실행하지 않음

 

 

 

resultMap은 생략하면 안되지만 parameterType은 리플렉션으로 읽어오기때문에 빼도 된다.

부등호 가들어가면 오류가 뜬다 왜냐면 태그 형태와 동일하게 인식하기 때문이다. <![CDATA[  ]]>안에 넣어주면 되는데 이러면 if문들어간거 까지 다 씹힌다 그래서 &gt; &lt; &le; 같이 html에서 쓰는 특수문자 기호로 작성한다. = 은 그대로 써준다

 

 

 

<include문>

이런식으로 공통된 쿼리부분을 <sql> 에 넣어서 id를 주고 <include refid="a"/> 로 원하는 위치에 넣어준다.

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

sql문 include도 있다.페이징에 적용하면 편리하다

 

<sql id="pagingBefore"> 
        select  O.* from (
            select rownum nrow, b.* from (
</sql>				
------------------------------------------------
<sql id="pagingAfter"> 
            ) b 
            where rownum &lt;= #{endNo}
        ) O
        where nrow between #{startNo} and #{endNo}
</sql>

include태그를 사용하면 이렇게 작성할 수있다.

<select id="getNoticeList" parameterType="java.util.HashMap" resultType="java.util.HashMap">
    <include refid="pagingBefore"/>
        select 
            a.* 
        from notice a

        <!-- 검색 조건 설정 하는 부분  -->
        <if test="searchTitle != '' and searchTitle != null">
            where title like concat(concat('%', #{searchTitle}), '%')
        </if>   

        order by title

    <include refid="pagingAfter"/>

</select>

 

원래 sql문

여기서 &lt;는 왜써주냐면 <는 태그로 인식되기때문에 CDATA를 써줬더니 쿼리문안에 썼던 if문이 다 씹혀서 이를 방지하기 위해서 '<' 문자 하나만 다르게 써줬다.

<select id="getNoticeList" parameterType="java.util.HashMap" resultType="java.util.HashMap">
    select  O.* from (
        select rownum nrow, b.* from (
            select 
                a.* 
            from notice a

            <!-- 검색 조건 설정 하는 부분  -->
            <if test="searchTitle != '' and searchTitle != null">
                where title like concat(concat('%', #{searchTitle}), '%')
            </if>   

            order by title
            ) b 
            where rownum &lt;= #{endNo}
        ) O
    where nrow between #{startNo} and #{endNo}
</select>

결과만을 보여주는거라면 sendRedirect이다. forward()하면 새로고침하면 계속 controller를 타기 때문에 같은 동작이 계속 반복된다.