MyBatis 사용 시에 param이 1개 혹은 여러개 혹은 전부 사용 시

prefix에 AND 혹은 , 항목이 필요 할 때 

trim 구문을 사용하면 유용하다




<update id="updateTable "  parameterType="map">

UPDATE TABLE

<trim prefix="SET" prefixOverrides=",">

<if test="aFlag!= null and aFlag!= ''.toString()">

, A_FLAG = #{aFlag}

</if>

<if test="bFlag!= null and bFlag!= ''.toString()">

, B_FLAG = #{bFlag}

</if>

<if test="cFlag!= null and cFlag!= ''.toString()">

, C_FLAG = #{cFlag}

</if>

</trim>

WHERE KEY = #{key}

</update>




<delete id="deleteMyInfo">
{call
declare
begin
DELETE FROM TABLE1 WHERE TABLE1 IN (SELECT TABLE1_NUM FROM TABLE1 WHERE MEM_ID = #{memId});
DELETE FROM TABLE2 WHERE TABLE2 IN (SELECT TABLE2_NUM FROM TABLE2 WHERE MEM_ID = #{memId});
DELETE FROM TABLE3 WHERE MEM_ID = #{memId};
end

</delete>


1. 자바에서 호출하는 경우

1CallableStatement cstmt = conn.prepareCall("{call PROC_BL_TO_UTM(?,?,?,?)}");
2cstmt.setString(1"37.465687");
3cstmt.setString(2"127.249481");
4cstmt.registerOutParameter(3, OracleTypes.FLOAT);
5cstmt.registerOutParameter(4, OracleTypes.FLOAT);
6cstmt.execute();
7uTmx = cstmt.getFloat(3);
8uTmy = cstmt.getFloat(4);


2. iBatis에서 프로시저 호출하기

SqlMap 설정

<parameterMap class="map" id="blParam">
  <parameter property="p_latitude" jdbcType="VARCHAR" javaType="java.lang.String" mode="IN"/>
  <parameter property="p_longitude" jdbcType="VARCHAR" javaType="java.lang.String" mode="IN"/>
  <parameter property="p_utmx" jdbcType="DECIMAL" javaType="long" mode="OUT"/>
  <parameter property="p_utmy" jdbcType="DECIMAL" javaType="long" mode="OUT"/>
 </parameterMap>
  
 <procedure id="bl_to_utm" parameterMap="blParam">
  <![CDATA[
   {call PROC_BL_TO_UTM(?,?,?,?)}
  ]]>
 </procedure>


java 설정

1Map<string, object=""> map = new HashMap<string,object>();
2map.put("p_latitude",vo.getX_latitude());
3map.put("p_longitude", vo.getX_longitude());
4sqlMapper.update("VocIphone.bl_to_utm", map );
5 
6String utmx = map.get("p_utmx").toString();
7String utmy = map.get("p_utmy").toString();</string,object></string,>



SqlMap 쪽에서 parameterMap 으로 설정해놓은 형식으로 프로시저에 전달되고 리턴은 데이터를 실어서 날렸던 맵으로 돌아옴
sqlMapper를 실행시킬때 update / queryForObject / queryForList 를 사용할수 있으니 리턴 타입에 맞춰서 사용하면 됨

out을 리스트로 받는 경우 resultMap을 설정해서 List 형태로도 받을수 있다. 

<parameter property="result" jdbcType="ORACLECURSOR" javaType="java.sql.ResultSet" resultMap="resultParam" mode="OUT"/>

resultMap을 설정해주고 resultMap에서 property와 column이름을 설정해주고 class를 미리 만들어놓은 VO객체로 넣어주면 해당항목의 리스트로 프로시저 실행결과가 리턴된다. 

3. 주의사항

sqlMap에서 프로시저를 호출할때

<procedure id="bl_to_utm" parameterMap="blParam">
   {
     call PROC_BL_TO_UTM(?,?,?,?)
    }
 </procedure>


위의 코드처럼 중괄호를 적어놓으면 에러가 난다..;; 괜히 보기 편하게 만든다고 했다가 삽질하게 된다. 

파라미터의 타입

만약 프로시저의 파라미터가 Number 타입이라면.. OUT 파라미터 정의에서 Number라고 쓰면 에러가 남
오라클의 경우 프로시저 내부에서 Number를 BigDecimal로 변환해서 사용한다고 한다. 따라서 아래처럼 적으면 에러가 난다. 

<parameter property="p_utmy" jdbcType="NUMBER" javaType="java.util.Number" mode="OUT"/>ERROR


<parameter property="p_utmy" jdbcType="DECIMAL" javaType="long" mode="OUT"/>

jdbcType="DECIMAL"이런 형태로 적어줘야 정상적으로 실행된다. 






출처 : http://rinn.kr/46


[applicationContext.xml]


<!-- ibatis -->
 <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
  <property name="configLocation">
   <value>classpath:sql/SqlMapConfig.xml</value>
  </property>
  <property name="dataSource" ref="dataSource"></property>
 </bean>
 
 <bean id="sqlMapClientTemplate" class="org.springframework.orm.ibatis.SqlMapClientTemplate">
  <property name="sqlMapClient" ref="sqlMapClient"/>
 </bean>

<bean id="transactionManager"
  class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource" />
</bean>




[HelloDaoImpl.java]

 


import java.sql.SQLException;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.ibatis.SqlMapClientTemplate;
import org.springframework.stereotype.Repository;

import com.tistory.devdiaries.dto.Test;

@Repository("helloDaoImpl")
public class HelloDaoImpl implements HelloDao {

 @Autowired
 SqlMapClientTemplate sqlMapClientTemplate;
 
 public void setSqlMapClientTemplate(SqlMapClientTemplate sqlMapClientTemplate) {
  this.sqlMapClientTemplate = sqlMapClientTemplate;
 }

 Test test;
 
 @Override
 public List<Test> getTestList() throws SQLException {
  
  return (List<Test>)sqlMapClientTemplate.queryForList("HelloSql.getTestList", test);
 }
 
}







 

  • 시나리오 환경
    TOMCAT 7.0 / JDK 1.7 / IBATIS / SPRING 3.0 / DBCP
  • 원인
    마지막으로 DB에 커넥션을 맺은 후 사용이 없다보니 데이터 베이스 커넥션의 부재
  • 해결책
    특정 시간마다 커넥션을 확인 하는 셋팅을 지정한다.

 

<bean id="dataSource1" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${jdbc.db1.driverClassName}" />
<property name="url" value="${jdbc.db1.url}" />
<property name="username" value="${jdbc.db1.username}" />
<property name="password" value="${jdbc.db1.password}" />

<!-- 특정시간 마다 validationQuery 실행 셋팅 -->
<property name="validationQuery" value="select 1" />
<property name="testWhileIdle" value="true" />
<property name="timeBetweenEvictionRunsMillis" value="7200000" />
<!-- // 특정시간 마다 validationQuery 실행 셋팅 -->
</bean>


혹은

<bean id="datasource" .. > 

...

<property name="validationQuery">

<value>SELECT 1</value>

</property>

<property name="testWhileIdle">

<value>true</value>

</property>

...

</bean>


참고 url - http://fbwotjq.tistory.com/entry/IBATIS-%EC%BB%A4%EB%84%A5%EC%85%98-%EC%97%90%EB%9F%AC



case 1) HashMap 형태


public void insertIntoImg(List<NeliVO> neliVoList) {

HashMap<String, Object> param = new HashMap<String, Object>();

param.put("List", neliVoList);

getSqlMapClientTemplate().insert(NAMESPACE  + "insertIntoImg", param);

}



<insert id="insertIntoImg" parameterClass="java.util.HashMap">

INSERT INTO ADMINIMG (

BOARD_SEQ

, CONTENTS_PATH

, CONTENTS_NAME

, CONTENTS_ORI_NAME

, CONTENTS_TYPE

, TEXT1

, REG_DATE

<dynamic>

<iterate prepend="VALUES" conjunction=", " property="List">

#List[].seqId# 

, #List[].path#

, #List[].fileName#

, #List[].oriFileName#

, #List[].contentsType#

, #List[].text1#

, now()

)

</iterate>

</dynamic>

</insert>





case 1) List 형태


public void insertIntoImg(List<NeliVO> neliVoList) {
getSqlMapClientTemplate().insert(NAMESPACE  + "insertIntoImg", neliVoList);
}


<insert id="insertIntoImg" parameterClass="java.util.List">

INSERT INTO ADMINIMG (

BOARD_SEQ

, CONTENTS_PATH

, CONTENTS_NAME

, CONTENTS_ORI_NAME

, CONTENTS_TYPE

, TEXT1

, REG_DATE

)  VALUES

<iterate conjunction=", ">

#[].seqId# 

, #List[].path#

, #List[].fileName#

, #List[].oriFileName#

, #List[].contentsType#

, #List[].text1#

, now()

)

</iterate>

</insert>

ibatis 에서 resultMap 형식으로 값을 vo에 담아 return 할 시, 간혹 값이 넘어 오지 않는 경우가 있다.

이 경우에는 resultMap property 값과 select 하고자 한 column 값과 정확히 일치 해주어야 return 이 가능해진다


무슨 말이냐.. 하면 예를 들어보자.


<resultMap id="BoardListResult" class="boardVo">

<result property="num" column="num" columnIndex="1" />

<result property="author" column="author" columnIndex="2" />

<result property="title" column="title" columnIndex="3" />

<result property="content" column="content" columnIndex="4" />

<result property="writeday" column="writeday" columnIndex="5" />

<result property="readcnt" column="readcnt" columnIndex="6" />

  <result property="repRoot" column="rep_root" columnIndex="7" /><!--이부분을 삭제-->

<result property="repIndent" column="rep_indent" columnIndex="8" /><!--당연히 columnIndex 값을 7 로 변경-->

</resultMap>


<select id="list" resultMap="BoardListResult" parameterClass="boardVo">

<![CDATA[

SELECT 

num

, author

, title

, content

, writeday

, readcnt

, rep_indent

FROM( 

            SELECT 

... 중략

</select>


위와 같이 했을 때 값을 return 해 올 수 없다. 그 이유는 select 한 값은 rep_indent 까지 딱 7개의 column을 들고 오는데 반해 resultMap은 repRoot라는 property 값이 선언 되어 있다.


이럴 때 select 할 때 rep_root를 찾던가, 아니면 resultMap에 선언 되어 있는 repRoot result 태그를 삭제 해주면 잘 return 하는 것을 볼 수 있다.

iBatis 는 실제적으로 많이 사용 하고 있거나, 그렇지 않으면 앞으로 사용 할 때

굉장히 헷갈리는 부분이 있다.


바로 어떻게 value object와 column name을 맵핑 시킬 것인가,

혹은 값들을 어떻게 jsp에 뿌릴 것 인가, 즉슨 자바 코드에 어떻게 담아서 운반 할 것인가

에 대해 많은 고민이 있다.


이제 때에 맞는 iBatis value 맵핑에 대해 알아보자.


우선, 게시판을 예로 들겠다.

1. 글 작성 후 글을 DB에 insert 시킬 시

여기선 아마 write.jsp 라는 페이지에서 form값을 vo에 담아 controller 에다 넘겨 줄 것이다.


// vo 객체를 인스턴스화 하여 가져온다

<typeAlias alias="boardVo" type="jh.board.vo.BoardVO"/>


<insert id="write" parameterClass="boardVo">


이렇게 parameterClass는 vo값을 insert만 하고 아무런 result값도 가져오지 않을 때 주로 쓰인다.


2. 글 목록들을 Select 할 때

여기선 객체 하나만 가져오는게 아니라, 객체의 리스트 제네릭을 리턴 받는다.


<resultMap id="BoardResult" class="boardVo">

<result property="num" column="num" columnIndex="1" />

<result property="author" column="author" columnIndex="2" />

<result property="title" column="title" columnIndex="3" />

<result property="content" column="content" columnIndex="4" />

<result property="writeday" column="writeday" columnIndex="5" />

<result property="readcnt" column="readcnt" columnIndex="6" />

</resultMap>


<select id="list" resultMap="BoardResult" parameterClass="boardVo">


resultMap을 이용하여 boardVo 객체의 겟/셋 변수명과 DB의 column 명을 맵핑 한 후

그 결과를 조회하여 resultMap 을 Controller단 까지 넘겨 받는다.


여기서 유심히 볼 것은 그저 select만 한다면 paramterClass가 필요 없다.

하지만 검색 시 검색명과 검색값을 select 문에 넣기 위해 paramterClass가 또 쓰여 졌다.


3. 상세글이나 업데이트 시


<select id="retrieve" resultMap="BoardResult">


<select id="updateForm" resultMap="BoardResult">


위와 마찬가지로 resultMap을 사용 하여 값을 return 받아 프레젠테이션 층에 넘겨 준다.


4. 그런데 resultClass 는 어디서 사용 될까 ?


<select id="count" resultClass="Integer">

SELECT 

COUNT( num )

FROM board

</select>


위와 같이 그냥 Integer(정수형) 으로 num의 count만 받고 싶다면 저렇게 쓰면 된다.


물론 Delete문과 같이 리턴 받을게 없고 그저 삭제만 시킨다 함은 resultMap, resultClass를 쓸 필요가 없는 것이다.


요약 하자면,

resultMap - Object를 return 받을 때 사용

resultClass - Integer 등 1개의 value형을 return 받을 때 사용

parameterClass - Object를 ibatis DML(#num#, #title#)문에 적용 시키고 싶을 때 사용



+ Recent posts