myBATIS : DynamicQuery 사용법

DynamicQuery부분에 사용한 옵션 외에 다양한 옵션이 제공 된다.
일반적으로 이런 옵션들을 이용할때 DAO에 쿼리가 박혀있을 경우 매우 복잡해진다.
따라서 스토어드 프로시저를 이용한다거나 하는 방식을 택하게 되는데, iBATIS에서 지원하는 옵션들을 살펴보자

————————————————————————————
단항연산 : IsNull, isNotNull, isEmpty, isNotEmpty
이항연산 : isEqual, isNotEqual, isGreaterThan, isGreaterEqual, isLessThan, isLessEqual
————————————————————————————

————————————————————————————

————————————————————————————

조건에 property는 당연히 들어가는것이 원칙이다.
비교할 대상이나 검사할 대상이 없다면 뭐하러 이런 조건을 사용 하겠는가?
어렵지 않은 내용이지만 한가지 숙지해야 할 사항이 있다.

바로 이항연산자 부분에서 compareValue와 compareProperty인데 다른 부분은 property를 이용해 비교했다.
같은 property가 들어가는걸로 보아 compareProperty는 프로퍼티 중에서 비교하는 것을알수 있다.
즉 ‘같이 넘어온 객체의 프로퍼티와 비교’하여 구문을 추가할지 선택하는것이다.

compareValue는 정적인 값이다. 예를들어 조건이 ‘성적이 100점 이하’일 경우 100은 정적인 값이다. 만약 이것을 compareProperty로 이용한다면 사용할때마다 ‘100’의 값을 셋팅해야할 것이다.
이럴때 compareValue를 이용하면 프로그램 코드에 별도로 관리할 필요가 없으므로 유용하게 사용할수 있다.
그럼 예제를 살펴보자.

boardManager.xml

SELECT
A.POST_ID AS POST_ID
,A.POST_TITLE AS POST_TITLE
,A.POST_WRITER AS POST_WRITER
,B.POST_BODY AS POST_BODY
FROM TB_MAIN_BOARD A, TB_SUB_BOARD B
WHERE A.POST_ID = B.POST_ID

11=1

a.post_id = #postId#

post_title like ‘%’ || #postTitle# || ‘%’

post_Writer like ‘%’ || #postWriter# || ‘%’

post_body like ‘%’ || #postBody# || ‘%’

insert into tb_main_board
values(#postId#,#postTitle#,#postWriter#)

insert into tb_sub_board(post_id, post_body)
values(#postId#,#postBody#)

BoardBean.java

package pupustory.ibatis.beans;
public class BoardBean {
String postId;
String postTitle;
String postWriter;
String postBody;
public String getPostBody() {
return postBody;
}
public void setPostBody(String postBody) {
this.postBody = postBody;
}
public String getPostId() {
return postId;
}
public void setPostId(String postId) {
this.postId = postId;
}
public String getPostTitle() {
return postTitle;
}
public void setPostTitle(String postTItle) {
this.postTitle = postTItle;
}
public String getPostWriter() {
return postWriter;
}
public void setPostWriter(String postWriter) {
this.postWriter = postWriter;
}
public String toString() {
return “postId[“+postId+”]”
+”postTitle[“+postTitle+”]”
+”postWriter[“+postWriter+”]”
+”postBody[“+postBody+”]”;
}
}
BoardBiz.java

package pupustory.ibatis.board;
import pupustory.ibatis.manager.DBManager;
import pupustory.ibatis.beans.BoardBean;
import java.sql.SQLException;
public class BoardBiz {
DBManager manager = DBManager.getInstance();
// 가상 데이터 삽입
public void insertVirData() throws SQLException{
BoardBean bean = null;
final int MAX_BOARD_DATA = 10;
manager.startTransaction();
manager.startBatch();
for (int i=0;i<MAX_BOARD_DATA;i++) {
bean = new BoardBean();
bean.setPostId(i+"");
bean.setPostTitle("제목! "+i);
bean.setPostWriter("pupustory"+i);
bean.setPostBody("본문 히히히히 헤헤"+i);
manager.getMapper().insert("insert.main.board", bean);
manager.getMapper().insert("insert.sub.board", bean);
}
manager.executeBatch();
manager.commitTransaction();
}
// 게시물 조회
public java.util.List getPost(BoardBean bean)
throws SQLException {
return
(java.util.List)manager.getMapper().queryForList("select.board",bean);
}
}

StartApp.java

package pupustory.ibatis.board;
import java.sql.SQLException;
import pupustory.ibatis.beans.BoardBean;
public class StartApp {
public static void main(String ar[]) throws SQLException{
BoardBiz biz = new BoardBiz();
//biz.insertVirData();
BoardBean bean = null;
java.util.List list = biz.getPost(bean);
for (int i=0;i<list.size();i++) {
System.out.println(list.get(i).toString());
}
bean = new BoardBean();
// id 3번 게시물 조회
bean.setPostId("3");
list = biz.getPost(bean);
for (int i=0;i<list.size();i++) {
System.out.println(list.get(i).toString());
}
// 본문에 '4'문자 포함한 게시물 조회
bean = new BoardBean();
//bean.setPostId("2");
bean.setPostBody("4");
list = biz.getPost(bean);
for (int i=0;i<list.size();i++) {
System.out.println(list.get(i).toString());
}
}
}
조회조건에 들어올 값을 확인하고, null이 아닐경우 검색해서 출력한다. equal같은 검색조건을 추가하려 했지만.. 귀찮기도해서 –; 어차피 사용법은 동일하다. 그리고 dynamic부분의 prepend는 삭제했다. 이유는 이미 join을 하기위해 where를 호출했고, 뒤에 올 조건이 있을지 없을지 미지수다. 따라서 삭제 했다. 1=1은 게시 전에 몇가지 만지작 거리다 남은 코드이므로 무시해도 무관하다.

단항,이항 검색조건은 위와 같이 수행하면 되므로 어려울 것이 없다. 그렇다면 이번엔 이터레이터에 대해 알아볼 것이다. 이전에 설명 했지만 만약 사용자가 ' where board_id in (….) '와 같이 몇건이 들어올지 모르는 경우에 유용 하다.

이부분은 $$를 이용한 statment를 이용할 수도 있겠지만 이건 매우 위험하다. sql injection때문에 그렇다. 어플에서 .replace했다면 상관 없겠지만 그래도 프레임워크를 이용하니 .. 기능을 이용해 보도록 하자.

먼저 IN ( …) 부분엔 정확히 얼마의 문자가 올지 모른다. 한가지 유추할 수 있는 것은 여기엔 배열이 적합한 것 이다. 새로운 예를 만드는 것 보다 위의 코드를 조금 변경해 작성하도록 하자. 먼저 빈즈에 POST_ID값이 저장될 배열 변수를 하나 추가한다.

private String rePostId;
public String getRePostId() {
return rePostId;
}
public void setRePostId(String rePostId) {
this.rePostId = rePostId;
}
public String toString() {//toString()는 값 확인을 위해 변경 함
return "postId [" +rePostId+"]"
+" postTitle [" +postTitle+"]"
+" postWriter [" +postWriter+"]"
+" postBody [" +postBody+"]"
+" parentPostId [" +parentPostId+"]";
}
이제 중요한 sqlmap부분이다.

SELECT * FROM TB_QNA_BOARD

#postId[]#

반복 부분을 iterator를 사용했다. 실행코드를 살펴보자.

package pupustory.ibatis.dynamicquery;
import pupustory.ibatis.beans.PostReplyBean;
public class StartApp {
public static void main(String ar[]) throws java.sql.SQLException {
PostReplyBean bean = new PostReplyBean();
DynamicQueryBiz biz = new DynamicQueryBiz();
String[] postIds = {“1″,”2″,”4″,”5″,”7″,”9”};
bean.setPostId(postIds);
java.util.List list = biz.getPostBoard(bean);
if (list.get(0) instanceof PostReplyBean) {
System.out.println(“true”);
}
for (int i=0;i

SELECT * FROM 테이블 명
WHERE 1 = 1

테이블명.name LIKE CONCAT(‘%’,#검색할문구#,’%’)

AND 다른 조건들 추가…

위 예에서 1=1 을 먼저 추가해준 이유는 dynamic 쿼리에서 에러가 있었을 때 여기에 prepend 로 WHERE를 걸었다면 그 문구가 아예 실행이 안되고 다음 AND 다른 조건들 로 넘어가게 된다. 그래서 mysql 문법 상 WHERE 없이 AND가 등장해서 문법에러가 나게 되므로 이를 방지하기 위해 WHERE를 조건을 먼저 주려고 1=1 이라는 것을 넣어 준 것이다.

[출처] http://netholic.tistory.com/99

——————————–

iBATIS 트렌젝션 사용시 주의사항

SqlMapClient sqlMap = SqlMapClientFactory.getInstance().getClient();

try {
sqlMap.startTransaction();
Object obj = sqlMap.queryForObject(“selectSeq”);
int createSeq = 0;
if(obj==null){
createSeq = 1;
}else{
createSeq = (Integer)obj;
createSeq += 1;
}

article.setSequenceNo(createSeq);
sqlMap.insert(“create”, article);
sqlMap.commitTransaction();
return createSeq;
}
catch (SQLException e)
{
throw new RuntimeException(“게시물 등록 실패”, e);
}
finally
{
//반드시 해줘야 합니다.안그러면 커넥션부족이 발생합니다.
sqlMap.endTransaction();
}

[출처] http://realcool.egloos.com/689804

————————————————————————–

iBATIS jUnit Test

@Autowired, @Transactional 을 사용하려면 아래와 같이 spring context에 반드시 들어가야 하며
ibatis 설정을 위한 sqlMapConfig.xml 의 경로를 설정해준다.

– sqlMapConfig.xml
가장 기본적인 설정만 가져왔으며 useStatementNamespaces가 default=false 이므로 true로 변경해줬다.

– game.xml , world.xml
이 두가지는 1대다 관계이다.
일단 join이 아닌 lazy loading 방식으로 구현해봤다.

game.xml

select * from GAME where GAME_ID = #value#

world.xml

select * from WORLD where WORLD_ID = #value# ORDER BY GAME_ID ASC

Game.java 는 아래와 같이 구성되어 있다.

private int gameId;
private int position;
private String name;private List worldList;
World.java 는 생략.

즉 나는 게임아이디로 쿼리를 때리면 그 게임과 거기 해당되는 worldList를 가져오게된다.
거기 해당되는 DAO쿼리 코드는 아래와 같다.

import java.util.List;
import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;

public class WorldDao extends SqlMapClientDaoSupport {
@SuppressWarnings(“unchecked”)
public List findById(int id) {
return getSqlMapClientTemplate().queryForList(“World.findById”, id);
}
}

이하는 junit4.4 + spring2.5 DAO 테스트코드다.

import java.util.List;

import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={“file:web/WEB-INF/context/global-test.xml”})
@TransactionConfiguration(transactionManager=”txManager”, defaultRollback=true)
public class WorldDaoTest {

private static final int WORLD_ID = 2;

@Autowired
private WorldDao worldDao;

@Test
public void findByWorldId() {
List worldList = worldDao.findById(WORLD_ID);
check(worldList.get(0));
}

private void check(World world) {
assertEquals(WORLD_ID, world.getWorldId());
}
}

[출처] iBATIS DynamicQuery 사용법 + etc|작성자 섹쉬구염가이

16 Replies to “myBATIS : DynamicQuery 사용법”

Leave a Reply

Your email address will not be published.