Spring으로 방명록 애플리케이션을 구현하는 시리즈입니다.
- [Spring] 방명록 애플리케이션 (1) - 환경 설정
- [Spring] 방명록 애플리케이션 (2) - 준비 단계 ( 스프링 활용하기 )
- [Spring] 방명록 애플리케이션 (3) - 구현
- [Spring] 방명록 애플리케이션 (4) - 정적 파일 처리 ( DefaultServletHandler )
- [Spring] 방명록 애플리케이션 (5) - 뷰 객체 생성 ( ViewResolver )
- [Spring] 방명록 애플리케이션 (6) - 예외 처리 ( ExceptionHandler )
- [Spring] 방명록 애플리케이션 (7) - 3 Layer Architecture와 Service 계층
- [Spring] 방명록 애플리케이션 (8) - 커넥션 풀 ( Connection Pool ) DBCP
- [Spring] 방명록 애플리케이션 (9) - Mybatis 환경 설정
- [Spring] 방명록 애플리케이션 (10) - Mybatis 적용
이전 글에서 Mybatis 환경 설정을 모두 마쳤습니다.
방명록 애플리케이션에 Mybatis를 적용하면 try - catch와 중복되는 코드가 많았던 DAO가 급격하게 줄어드는 것을 확인할 수 있을 것입니다.
Mybatis가 중복되는 코드를 Template 기법을 적용하여 제거해주기 때문입니다.
이제 이 마법을 살펴보도록 하겠습니다.
우선 SQL Mapping을 위해서는 DAO의 각 메서드마다 다음의 3가지를 확인해야 합니다.
=> 파라미터 / sql / 반환 타입
GuestBookDAO.getList() 변경
GuestBookDAO의 getList() 메서드를 보면 파라미터는 없고 , 반환 타입은 GuestBookVO 입니다.
1)
이를 SQL Mapping 하면 다음과 같이 바꿀 수 있습니다.
/src/main/resources/mybatis/mappers/guestbook.xml
<mapper namespace="guestbook">
<select id="getList" resultType="guestbookvo">
<![CDATA[
SELECT no, name, pwd, content, reg_date as regDate
FROM guestbook
ORDER BY no desc
]]>
</select>
</mapper>
SELECT 쿼리이므로 <select>를 사용합니다.
id는 mapping의 이름을 의미하고,
resultType은 반환형 타입을 의미합니다.
![CDATA[ ]]는 부등호 > , <가 있을 경우 태그의 열고 닫는 기호로 인식 될 수 있기 때문에, ![CDATA[ ]]를 통해 쿼리가 작성되는 곳이라고 선언하는 것입니다.
이 때 주의할 점은 칼럼 명이 GuestBookVO의 인스턴스 변수 이름과 같아야 한다는 것입니다.
내부적으로 getter, setter가 호출되어 mapping 되기 때문에 칼럼 명과 변수 명이 같아야 합니다.
그래서 DB에서는 reg_date라는 이름의 칼럼을 갖지만 GuestBookVO에서는 변수명이 regDate이므로 alias를 통해 칼럼명을 변경했습니다.
( DB의 칼럼 명과 Java에서는 변수 명은 관례를 따르는 것이 좋습니다. )
2)
resultType의 guestbookvo는 사실 패키지 명까지 작성을 해야 합니다.
그런데 패키지명까지 작성하는 것은 코드가 길기 때문에, 축약을 위해 상위 폴더 configuration.xml에서 별칭을 붙였습니다.
/src/main/resources/mybatis/configuration.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 alias="guestbookvo" type="com.victolee.guestbook.vo.GuestBookVO"></typeAlias>
</typeAliases>
<mappers>
<mapper resource="mybatis/mappers/guestbook.xml" />
</mappers>
</configuration>
<typeAliases> 태그 아래에 <typeAlias> 태그로 별칭을 붙일 수 있습니다.
3)
이제 GuestBookDAO의 코드를 줄여보겠습니다.
GuestBookDAO
@Repository public class GuestBookDAO { @Autowired private DataSource dataSource; @Autowired private SqlSession sqlSession; public List<GuestBookVO> getList() throws GuestbookExcpetion{ List<GuestBookVO> list = sqlSession.selectList("guestbook.getList"); return list; } }
getList() 메서드만 바꿨고, 나머지 메서드는 일단 생략했습니다.
보시면 sqlSession.selectList() 메서드를 호출하여 게시글 목록들을 가져올 수 있습니다.
sqlSession 객체는 applicationContext.xml 파일에서 bean 객체로 등록을 한 것이고,
selectList() 메서드는 sqlSession 객체가 갖고 있는 메서드로서 List형태로 데이터를 가져오는 메서드입니다.
selectList() 메서드의 매개변수인 guestbook.getList 문자열은 namespace가 guestbook인 mapper에서 getList를 id로 갖는 쿼리를 실행하라는 의미입니다.
그러면 게시글 목록을 List 타입으로 반환할 것입니다.
GuestBookDAO 변경
이런 식으로 SQL Mapping이 이루어집니다.
이제 전체 코드를 작성 해보고 추가적인 부분을 살펴보겠습니다.
guestbook.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="guestbook">
<select id="getList" resultType="guestbookvo">
<![CDATA[
SELECT no, name, pwd, content, reg_date as regDate
FROM guestbook
ORDER BY no desc
]]>
</select>
<select id="getOriginPwd" parameterType="Integer" resultType="string">
<![CDATA[
SELECT pwd FROM guestbook WHERE no = #{ no }
]]>
</select>
<select id="getInputPwd" parameterType="string" resultType="string">
<![CDATA[
SELECT password(#{ pwd })
]]>
</select>
<insert id="insert" parameterType="guestbookvo">
<![CDATA[
INSERT INTO guestbook
VALUES (null, #{ name }, password(#{ pwd }), #{ content }, (SELECT SYSDATE()) )
]]>
</insert>
<delete id="delete" parameterType="Integer">
<![CDATA[
DELETE FROM guestbook WHERE no= #{ no }
]]>
</delete>
</mapper>
1)
service 계층에서 파라미터를 전달할 경우 guestbook.xml 파일에서 parameterType으로 받을 수 있습니다.
VO객체를 통째로 받을 수도 있고, 래퍼 클래스, string( 소문자 )으로도 받을 수 있습니다.
2)
파라미터는 EL 표기법으로 받을 수 있습니다.
래퍼 클래스, string인 경우에는 #{ 아무 이름 }으로 받을 수 있습니다만, 가독성이 좋게 정확한 이름을 작성하는 것이 좋습니다.
객체를 받을 경우에는 #{ 인스턴스 변수 이름 }으로 객체의 값을 전달할 수 있습니다.
단, 그 객체에 getter , setter가 존재해야 합니다.
GuestBookDAO
@Repository public class GuestBookDAO { @Autowired private DataSource dataSource; @Autowired private SqlSession sqlSession; public List<guestbookvo> getList() throws GuestbookExcpetion{ List<guestbookvo> list = sqlSession.selectList("guestbook.getList"); return list; } public String getPwd(Integer no) throws GuestbookExcpetion { String pwd = sqlSession.selectOne("guestbook.getOriginPwd", no); return pwd; } public String getInputPwd(String pwd) throws GuestbookExcpetion{ String parsePwd = sqlSession.selectOne("guestbook.getInputPwd", pwd); return parsePwd; } public boolean insert(GuestBookVO vo ) throws GuestbookExcpetion{ int count = sqlSession.insert("guestbook.insert", vo); return count == 1; } public boolean delete(Integer no) throws GuestbookExcpetion{ int count = sqlSession.delete("guestbook.delete", no); return count == 1; } }
selectOne() 메서드는 하나의 결과 값을 조회할 때 사용합니다.
또한 sqlSession객체에는 insert , delete , update 메서드가 구현되어 있어서 CRUD가 가능합니다.
보시는 바와 같이 DAO가 정말 간단해진 것을 확인할 수 있습니다.
SQL 쿼리 부분도 하나의 설정 파일로 만들어 놓으니 관리도 더 편해질 것 같습니다.
응용
지금까지 방명록 애플리케이션을 구현하는데 필요한 Mybatis의 기본적인 CRUD만 알아보았습니다.
이 밖에 더 많은 기능들을 제공하는데, 그 중 자주 사용했던 몇 가지를 소개해보려고 합니다.
1) 방금 insert 한 row의 특정 칼럼 값 가져오기
클라이언트로부터 GuestBookVO 객체를 전달 받아 insert를 수행할 때, PK인 no 값은 auto increment이기 때문에 null을 작성했습니다.
insert를 수행하면 데이터가 추가되었으므로 no 값이 생길 것인데, 그냥 insert만 수행했을 때는 insert를 수행했던 GuestBookVO의 no 값을 바로 가져올 수 없습니다.
그런데 <selectKey>를 이용하면 service 계층에서 곧바로 no 값을 가져올 수 있습니다.
guestbook.xml
<insert id="insert" parameterType="guestbookvo">
<![CDATA[
INSERT INTO guestbook
VALUES (null, #{ name }, password(#{ pwd }), #{ content }, (SELECT SYSDATE()) )
]]>
<selectKey keyProperty="no" resultType="Integer" order="AFTER">
SELECT last_insert_id()
</selectKey>
</insert>
GuestBookService
public GuestBookVO insert(GuestBookVO guestBookVO ) { GuestBookVO vo = null; int count = guestBookDAO.insert(guestBookVO); if( count == 1) { vo = guestBookDAO.get( guestBookVO.getNo() ); } return vo; }
2) Mybatis에 파라미터로 여러 객체를 전달
이 글의 GuestBookDAO를 보시면 Mybatis로 파라미터를 전달할 때 Integer , String , GuestBookVO를 전달했습니다.
그런데 전달해야 할 파라미터가 많을 경우에는 어떻게 할까요?
예를들어, 블로그 제목, 소개 글, 기본 이미지를 기본 값으로 설정하여 블로그를 생성하고 싶을 수 있습니다.
그러기 위해서는 각각의 문자열을 미리 세팅한 후 Mybatis에 넘겨줘야 하는데 지금까지의 방법으로는 하나의 파라미터만 전달이 가능하므로 해결 할 수 없습니다. ( GuestBookVO는 여러 변수가 존재하지만 파라미터 관점에서 GuestBookVO 객체 하나입니다. )
즉 Mybatis에 여러 객체를 파라미터로 전달하고 싶을 경우에는 Map을 사용합니다.
GuestBookDAO
public boolean insert() { Map<String, Object> map = new HashMap<String, Object>(); map.put("title", "가입을 환영합니다 ~!"); map.put("intro", "소개 글을 작성해주세요."); map.put("img", "https://image.flaticon.com/icons/svg/145/145867.svg"); return sqlSession.insert("blog.insert", map) == 1; }
Mybatis에서 사용하고 싶은 데이터를 Map의 형태로 작성한 후, map 객체를 전달하면 됩니다.
guestbook.xml
<insert id="insert" parameterType="hashmap">
<![CDATA[
INSERT INTO blog VALUES (null, #{title}, #{intro}, #{img})
]]>
</insert>
Mybatis에서는 parameterType으로 hashmap 을 작성한 후, 데이터는 전달 받은 map 객체의 key 값을 사용하면 됩니다.
이상으로 방명록 애플리케이션을 구현하는 주제를 모두 마치도록 하겠습니다.
지금까지 방명록 애플리케이션을 만들면서 스프링 및 여러 라이브러리 및 설정에 대해 알아보았습니다.
Mybatis를 적용한 방명록 애플리케이션 1 ~ 10까지의 최종 코드는 깃헙에 올렸습니다. ( 링크 )
이후의 글에서는 조금 더 advance한 스프링을 알아볼 것이고, Mybatis보다 더 쉽게 쿼리를 작성할 수 있는 JPA에 대해서도 알아볼 것입니다.