이전에 작성했던 "Embedded Databases 사용하기 - <jdbc:embedded-database>"에서는 Test Case 작성 시 JdbcTemplate 을 사용했었다. 이 예제에서는 JdbcTemplate 대신 MyBatis 를 사용하도록 변경하고, select 외에 insert/update/delete 기능들도 추가로 테스트해 보도록 하겠다. 따라서 "Embedded Databases 사용하기 - <jdbc:embedded-database>" 를 먼저 읽어보는 것이 좋다.
Maven Dependencies
"Embedded Databases 사용하기 - <jdbc:embedded-database>" 예제의 Dependency 에 mybatis, mybatis-spring, log4jdbc-remix, slf4j-log4j12 를 추가하였다. mybatis 와 mybatis-spring 은 mybatis 를 사용하기 위한 필수 Dependency 이며, log4jdbc-remix, slf4j-log4j12 는 선택사항이다. log4jdbc-remix, slf4j-log4j12 를 추가한 이유에 대해서는 log4j.xml 설정을 설명할 때 언급하도록 하겠다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75 |
<properties> <org.springframework-version> 4.0 . 6 .RELEASE</org.springframework-version> <hsqldb-version> 1.8 . 0.10 </hsqldb-version> <mybatis-version> 3.2 . 7 </mybatis-version> <mybatis.spring-version> 1.2 . 2 </mybatis.spring-version> <org.slf4j-version> 1.7 . 7 </org.slf4j-version> <log4jdbc-version> 0.2 . 7 </log4jdbc-version> <junit.version> 4.10 </junit.version> <org.hamcrest.version> 1.1 </org.hamcrest.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${org.springframework-version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${org.springframework-version}</version> </dependency> <dependency> <groupId>hsqldb</groupId> <artifactId>hsqldb</artifactId> <version>${hsqldb-version}</version> </dependency> <!-- MyBatis Dependencies --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>${mybatis-version}</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>${mybatis.spring-version}</version> </dependency> <!-- Log Dependencies--> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>${org.slf4j-version}</version> </dependency> <dependency> <groupId>org.lazyluke</groupId> <artifactId>log4jdbc-remix</artifactId> <version>${log4jdbc-version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${org.springframework-version}</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-all</artifactId> <version>${org.hamcrest.version}</version> <scope>test</scope> </dependency> |
applicationContext.xml 설정
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47 |
<?xml version= "1.0" encoding= "UTF-8" ?> <beans xmlns= "http://www.springframework.org/schema/beans" xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc= "http://www.springframework.org/schema/mvc" xmlns:context= "http://www.springframework.org/schema/context" xmlns:p= "http://www.springframework.org/schema/p" xmlns:oxm= "http://www.springframework.org/schema/oxm" xmlns:jdbc= "http://www.springframework.org/schema/jdbc" xsi:schemaLocation=" http: //www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http: //www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd http: //www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http: //www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.0.xsd"> <context:component-scan base- package = "com.happyhouse.rednics" /> <jdbc:embedded-database id= "embdDataSource" type= "HSQL" > <jdbc:script location= "classpath:/schema.sql" /> <jdbc:script location= "classpath:/data.sql" /> </jdbc:embedded-database> <!-- Custom sql formatter --> <bean id= "dataSource" class = "net.sf.log4jdbc.Log4jdbcProxyDataSource" > <constructor-arg ref= "embdDataSource" /> <property name= "logFormatter" > <bean class = "net.sf.log4jdbc.tools.Log4JdbcCustomFormatter" > <property name= "loggingType" value= "MULTI_LINE" /> <property name= "sqlPrefix" value= "[SQL] " /> </bean> </property> </bean> <bean id= "sqlSessionFactory" class = "org.mybatis.spring.SqlSessionFactoryBean" > <property name= "dataSource" ref= "dataSource" /> <property name= "configLocation" value= "classpath:/mybatis.xml" /> <property name= "typeAliasesPackage" value= "com.happyhouse.rednics.tutorial.spring.mybatis.dto" /> </bean> <bean id= "sqlSession" class = "org.mybatis.spring.SqlSessionTemplate" destroy-method= "clearCache" > <constructor-arg index= "0" ref= "sqlSessionFactory" /> </bean> <bean class = "org.mybatis.spring.mapper.MapperScannerConfigurer" > <property name= "basePackage" value= "com.happyhouse.rednics.tutorial.spring.mybatis.dao" /> </bean> </beans> |
"Embedded Databases 사용하기 - <jdbc:embedded-database>" 에 추가된 내용은 다음과 같다.
1. Log4jdbcProxyDataSource 빈 추가 (옵션)
SQL 로그 포멧을 커스터마이징 하기 위해서 추가했다. <jdbc:embedded-database> 태그로 정의한 dataSource(embdDataSource) 를, 생성자의 파라메터로 받고 있다. 이 빈은 SqlSessionFactoryBean 의 dataSource 프로퍼티에 설정한다. SQL 로그 커스터 마이징 기능을 원하지 않는다면 <jdbc:embedded-database> 로 정의한 dataSource 를 SqlSessionFactoryBean 에 바로 설정하는 것도 가능하다.
2. SqlSessionFactoryBean 빈 추가 (필수)
MyBatis 를 사용하기 위해서는 꼭 추가해야 할 빈 중에 하나이다. configLocation 프로퍼티에는 mybatis 설정 파일의 위치를 지정한다. typeAliasesPackage 프로퍼티에는 DTO 클래스가 존재하는 패키지명을 콤마로 구분해 가면서 설정하면 된다. typeAliasesPackage 프로퍼티에 com.happyhouse.rednics.tutorial.spring.mybatis.dto 를 설정하게 되면, 이 패키지 밑에 존재하는 클래스의 이름을 Mapper 정의 파일(UserDao.xml)의 resultType 과 parameterType 어트리뷰트에 바로 사용할 수 있게 된다.아래 작성된 UserDao.xml 파일의 resultType 과 parameterType 에서 사용되는 "User" 라는 값에 주목하자. 따라서 이 예제에서는 mybatis 설정파일에 <typeAliases> 및 하위의 <typeAlias> 태그를 사용할 필요가 없다.
3. SqlSessionTemplate 빈 추가 (필수)
SqlSessionFactoryBean 과 함께 MyBatis 를 사용하기 위해서 꼭 추가해야 하는 빈이다. SqlSessionFactoryBean 으로 생성한 빈을 생성자 파라메터로 받고 있다.
4. MapperScannerConfigurer 추가 (옵션이나, 개인적으로 필수적인 빈이라 생각함)
아래 MyBatis 설정파일을 보면 일반적으로 사용하는 <mappers> 와 하위의 <mapper> 태크를 사용하지 않고 있다. MapperScannerConfigurer 를 설정했기 때문에 사용할 필요가 없는 것이다. basePackage 프로퍼티에 Mapper 설정파일이 존재하는 패키지명들을 콤마로 구분하여 설정하면, 자동으로 검색되기 때문이다. 이렇게 자동설정된 Mapper(UserDao) 는 서비스의 구현체등에 Autowiire 를 통해서 DI 도 가능하게 된다. MapperScannerConfigurer 를 사용함으로써 아래 UserService 구현체 소스에서 UserDAO 가 바로 DI 가능하게 되었음에 주목하자.
mybatis.xml 설정 (MyBatis 설정파일)
이 설정 파일에서는 <typeAliases> 와 <mapper> 태그를 사용하지 않는다. SqlSessionFactoryBean 의 typeAliasesPackage 프로퍼티와 MapperScannerConfigurer 의 basePackage 프로퍼티 설정을 사용해서 생략 가능한 태그들이기 때문이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14 |
<?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> <settings> <setting name= "cacheEnabled" value= "true" /> <setting name= "lazyLoadingEnabled" value= "true" /> <setting name= "multipleResultSetsEnabled" value= "true" /> <setting name= "useColumnLabel" value= "true" /> <setting name= "defaultStatementTimeout" value= "25000" /> </settings> </configuration> |
schema.sql 작성
1
2
3
4
5
6
7
8
9
10 |
create schema happyhouse AUTHORIZATION DBA; set schema happyhouse; drop table tbl_user if exists; CREATE TABLE tbl_user ( id varchar( 40 ) NOT NULL, username varchar( 45 ) NOT NULL, password varchar( 45 ) NOT NULL ); |
data.sql 작성
1 |
insert into tbl_user values( 'rednics' , 'Shin Kwan Young' , '1234' ); |
User 클래스 작성 (DTO)
위에서 생성한 tbl_user 테이블의 컬럼정의와 동일한 User 클래스를 작성한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 |
package com.happyhouse.rednics.tutorial.spring.mybatis.dto; public class User { private String id; private String username; private String password; public String getId() { return id; } public void setId(String id) { this .id = id; } public String getUsername() { return username; } public void setUsername(String username) { this .username = username; } public String getPassword() { return password; } public void setPassword(String password) { this .password = password; } } |
UserDao 인터페이스 작성 (MyBatis Mapper 인터페이스)
1
2
3
4
5
6
7
8
9
10
11
12
13 |
package com.happyhouse.rednics.tutorial.spring.mybatis.dao; import java.util.List; import com.happyhouse.rednics.tutorial.spring.mybatis.dto.User; public interface UserDao { public List<User> selectAllUsers(); public int insertUser(User user); public int updateUser(User user); public int deleteUser(User user); } |
UserDao.xml 설정 (MyBatis Mapper 설정파일)
위에서 설명했지만 resultType 과 parameterType 에 설정하고 있는 "User" 는, SqlSessionFactoryBean 설정의 typeAliasesPackage 프로퍼티에 설정한 패키지 내부에 존재하는 User 라는 클래스의 이름이다. MyBatis 설정파일에 <typeAliases> 태크를 사용하지 않고도 typeAlias 와 동일한 효과를 볼 수 있는 것이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 |
<?xml version= "1.0" encoding= "UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://www.mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace= "com.happyhouse.rednics.tutorial.spring.mybatis.dao.UserDao" > <select id= "selectAllUsers" resultType= "User" > SELECT * FROM happyhouse.tbl_user </select> <insert id= "insertUser" parameterType= "User" > INSERT INTO happyhouse.tbl_user VALUES( #{id}, #{username}, #{password} ) </insert> <update id= "updateUser" parameterType= "User" > UPDATE happyhouse.tbl_user SET username = #{username}, password = #{password} WHERE id = #{id} </update> <delete id= "deleteUser" parameterType= "User" > DELETE FROM happyhouse.tbl_user WHERE id = #{id} </delete> </mapper> |
UserService 인터페이스 작성
1
2
3
4
5
6
7
8
9
10
11
12
13 |
package com.happyhouse.rednics.tutorial.spring.mybatis.service; import java.util.List; import com.happyhouse.rednics.tutorial.spring.mybatis.dto.User; public interface UserService { public List<User> selectAllUsers(); public int insertUser(User user); public int updateUser(User user); public int deleteUser(User user); } |
UserServiceImpl(UserService 구현체) 작성
MapperScannerConfigurer 빈 설정을 통해 UserDao(MyBatis Mapper) 가 UserService 구현체에 직접 DI 되고 있는 점에 주목하자
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37 |
package com.happyhouse.rednics.tutorial.spring.mybatis.service; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.happyhouse.rednics.tutorial.spring.mybatis.dao.UserDao; import com.happyhouse.rednics.tutorial.spring.mybatis.dto.User; @Service public class UserServiceImpl implements UserService { @Autowired private UserDao userDao; @Override public List<User> selectAllUsers() { return userDao.selectAllUsers(); } @Override public int insertUser(User user) { return userDao.insertUser(user); } @Override public int updateUser(User user) { return userDao.updateUser(user); } @Override public int deleteUser(User user) { return userDao.deleteUser(user); } } |
Test Case 작성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56 |
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 com.happyhouse.rednics.tutorial.spring.mybatis.dto.User; import com.happyhouse.rednics.tutorial.spring.mybatis.service.UserService; @RunWith (SpringJUnit4ClassRunner. class ) @ContextConfiguration ( "/applicationContext.xml" ) public class MyBatisTest { @Autowired private UserService userService; @Test public void test() { userService.selectAllUsers(); User user = new User(); addUser(user); userService.selectAllUsers(); modifyUser(user); userService.selectAllUsers(); removeUser(user); userService.selectAllUsers(); } private void addUser(User user) { user.setId( "id2" ); user.setUsername( "username2" ); user.setPassword( "password2" ); userService.insertUser(user); } private void modifyUser(User user) { user.setId( "id2" ); user.setUsername( "username_modified" ); user.setPassword( "password_modified" ); userService.updateUser(user); } private void removeUser(User user) { user.setId( "id2" ); user.setUsername( "username_modified" ); user.setPassword( "password_modified" ); userService.deleteUser(user); } } |
log4j.xml 설정
Maven Dependencies 에서 설명할 때 log4jdbc-remix, slf4j-log4j12 는 선택항목이라고 말한적이 있다. 이 두 Dependency 와 아래의 log4j 설정을 사용하게 되면 각 SQL 및 파라메터를 로그로 확인할 수 있으며 select 쿼리의 실행결과인 ResultSet 을 아래 실행결과와 같이 테이블형태로 볼 수 있게 된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 |
<?xml version= "1.0" encoding= "UTF-8" ?> <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd" > <log4j:configuration xmlns:log4j= "http://jakarta.apache.org/log4j/" > <appender name= "console" class = "org.apache.log4j.ConsoleAppender" > <param name= "Target" value= "System.out" /> <layout class = "org.apache.log4j.PatternLayout" > <param name= "ConversionPattern" value= "%d{[yyyy-MM-dd] [HH:mm:ss]} [%-5p] [%t] [%s] [%C] [%M] : %m%n" /> </layout> </appender> <logger name= "jdbc.resultsettable" > <level value= "info" /> </logger> <logger name= "jdbc.sqlonly" > <level value= "info" /> </logger> <logger name= "net.sf.log4jdbc" > <level value= "info" /> </logger> <logger name= "com.happyhouse.rednics.tutorial.spring.mybatis.service" > <level value= "info" /> </logger> <root> <priority value= "info" /> <appender-ref ref= "console" /> </root> </log4j:configuration> |
실행결과
실재 로그는 select/insert/update/delete 관련 SQL, 관련 파라메터 및 다양한 디버깅 정보등을 포함하고 있으나 여기서는 주요 정보만을 추려서 보여주고 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 |
1 . addUser 수행 전 |--------|----------------|---------| |ID |USERNAME |PASSWORD | |--------|----------------|---------| |rednics |Shin Kwan Young | 1234 | |--------|----------------|---------| 2 . addUser 수행 후 |--------|----------------|----------| |ID |USERNAME |PASSWORD | |--------|----------------|----------| |rednics |Shin Kwan Young | 1234 | |id2 |username2 |password2 | |--------|----------------|----------| 3 . modifyUser 수행 후 |--------|------------------|------------------| |ID |USERNAME |PASSWORD | |--------|------------------|------------------| |rednics |Shin Kwan Young | 1234 | |id2 |username_modified |password_modified | |--------|------------------|------------------| 4 . removeUser 수행 후 |--------|----------------|---------| |ID |USERNAME |PASSWORD | |--------|----------------|---------| |rednics |Shin Kwan Young | 1234 | |--------|----------------|---------| |
'SQL > HSQLDB' 카테고리의 다른 글
HSQL 관리 툴 (0) | 2015.03.04 |
---|---|
Embedded Databases 사용하기 - <jdbc:embedded-database> (0) | 2015.03.04 |