> Hello World !!!

     

@syaku

스프링 JdbcTemplate HSQLDB : Spring Framework : 스프링프레임워크 #1

written by Seok Kyun. Choi. 최석균



스프링 JdbcTemplate HSQLDB : Spring Framework : 스프링프레임워크 #1

개발환경

Mac OS X 10.9.4
JAVA 1.6
Apache Tomcat 7.x
Spring 3.1.1
Spring Tool Suite 3.5.1
Maven 2.5.1
HSQLDB 2.3.2
RazorSQL

다양한 DBMS를 지원하는 GUI TOOL RazorSQL 설치하여 사용한다. 모든 운영체제를 지원한다.

http://razorsql.com

HSQLDB, H2, Derby 는 자바언어로 개발된 내장형 디비이다. 내장형이라 따로 설치할 필요가 없고 자바 머신이 구동되는 어떤 서비스든 사용할 수 있다.
어떤 디비가 더 좋고 나쁜지는 잘 모르겠으나, HSQLDB를 먼저 접했어 이것으로 사용하였다.
자세한 설명은 검색을 통해 직접 알아보도록 한다.

HSQLDB : http://hsqldb.org
H2 : http://www.h2database.com
Derby : http://db.apache.org/derby

HSQLDB 사이트에 공개된 성능표이다.



STS 에 스프링 프로젝트를 생성하고, 필요한 디펜덴시와 기본 설정은 아래와 같다.

@소스 pom.xml

<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.3.2</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework-version}</version>
</dependency>

@소스 src/main/webapp/WEB-INF/spring/root-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

     <!-- Root Context: defines shared resources visible to all other web components -->
     <import resource="classpath:config-context.xml"/>
</beans>

HSQLDB 는 java 명령어를 이용하여 서버를 구동할 수 있다. 하지만 난 서비스가 시작될때 구동될 수 있게 구현하였다. (java를 이용한 구동법은 검색하면 쉽게 찾을 수 있다.) 아래의 클래스가 HSQLDB 시작 및 종료를 담당하는 클래스이다.

@소스 src/main/java/com/syaku/hsqldb/HSQLDB.java

package com.syaku.hsqldb;

import java.io.IOException;
import java.util.Properties;

import org.hsqldb.Server;
import org.hsqldb.persist.HsqlProperties;
import org.hsqldb.server.ServerAcl.AclFormatException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.SmartLifecycle;

public class HSQLDB implements SmartLifecycle {

     private final Logger logger = LoggerFactory.getLogger(HSQLDB.class);
     private HsqlProperties properties;
     private Server server;
     private boolean running = false;

     public HSQLDB(Properties props) {
          properties = new HsqlProperties(props);
     }

     @Override
     public boolean isRunning() {
          if(server != null)
               server.checkRunning(running);
               return running;
          }

     @Override
     public void start() {
          if(server == null) {
               logger.info("Starting HSQL server...");
               server = new Server();

               try {
                    server.setProperties(properties);
                    server.start();
                    running = true;
               } catch(AclFormatException afe) {
                    logger.error("Error starting HSQL server.", afe);
               } catch (IOException e) {
                    logger.error("Error starting HSQL server.", e);
               }
          }
     }

     @Override
     public void stop() {
          logger.info("Stopping HSQL server...");
          if(server != null) {
               server.stop();
               running = false;
          }
     }

     @Override
     public int getPhase() {
          return 0;
     }

     @Override
     public boolean isAutoStartup() {
          return true;
     }

     @Override
     public void stop(Runnable runnable) {
          stop();
          runnable.run();
     }

}

그리고 스프링 서비스가 구동될때 서버가 시작될 수 있게 컨텍스트에 설정한다.

@소스 src/main/resources/config-context.xml

<?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:util="http://www.springframework.org/schema/util"
     xmlns:context="http://www.springframework.org/schema/context"
     xmlns:p="http://www.springframework.org/schema/p"
     xmlns:jdbc="http://www.springframework.org/schema/jdbc"
     xsi:schemaLocation="
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
     http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
     http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
     ">

     <!-- HSQLDB STARTER -->
     <bean id="hsqldb" class="com.syaku.hsqldb.HSQLDB" init-method="start">
          <constructor-arg>
               <value>
               server.database.0 = file:/Users/syaku/develop/spring/hsqldb/syaku
               server.dbname.0 = syaku
               server.remote_open = true
               server.port = 9002
               hsqldb.reconfig_logging = false
               </value>
          </constructor-arg>
     </bean>

     <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
          <property name="driverClassName" value="org.hsqldb.jdbcDriver" />
          <property name="url" value="jdbc:hsqldb:hsql://localhost:9002/syaku" />
          <property name="username" value="sa" />
          <property name="password" value="" />
     </bean>

     <!-- CREATE TABLE -->
     <jdbc:initialize-database data-source="dataSource" ignore-failures="DROPS">
     <jdbc:script location="classpath:db-schema.sql" />
     </jdbc:initialize-database>

     <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
          <property name="dataSource" ref="dataSource"/>
     </bean>

</beans>

위와같이 하면 서비스가 구동되면서 HSQLDB도 같이 시작된다. 하지만, 개발시 사용할경우 다양한 문제점이 발생한다.
서비스를 리로드할 경우 디비 서버도 같이 종료되고 시작되어야 하는 데 그렇지 않아 문제가 발생하여 컴파일할때마다 서비스를 종료했다 시작해야하는 불편함이 있고,
jUnit 또한 문제가 생긴다~ 그래서 개발모드에서는 터미널로 이용한 HSQLDB 서버를 시작하는 것이 좋다.

java -cp hsqldb.jar org.hsqldb.server.Server --database.0 file:/Users/syaku/develop/spring/hsqldb/syaku --dbname.0 syaku --remote_open true

그외 옵션

OPTION TYPE DEFAULT DESCRIPTION
—help printsthismessage
—address name/number any serverinetaddress
—port number 9001/544 portatwhichserverlistens
—database.i [type]spec 0=test pathofdatabasei
—dbname.i alias urlaliasfordatabasei
—silent true/false true false=>displayallqueries
—trace true/false false displayJDBCtracemessages
—tls true/false false TLS/SSL(secure)sockets
—no_system_exit true/false false donotissueSystem.exit()
—remote_open true/false false canopendatabasesremotely
—props filepath filepathofpropertiesfile

출처 : http://hsqldb.org/doc/src/org/hsqldb/server/Server.html

이렇게하면 9002포트로 접속이 가능하다. 위 방법을 사용할 경우 스프링 컨텍스트 hsqldb 를 주석처리하면 된다.

hsqldb HSQLDB 서버를 시작과 종료를 담당한다. 파일방식을 사용하였다. (HSQLDB 는 다양한 디비방식을 지원한다.)
server.database 는 절대 경로를 지정해야 한다.
server.remote_open 은 외부접속을 허용할 수 있다. 9002 포트를 이용하여 RazorSQL 툴로 접속하면 된다.

dataSource HSQLDB 연결을 담당한다.

url 에 아래와 같이 옵션을 추가하면, syaku 라는 데이터베이스가 없는 경우 오류가 발생하게 한다. 반대로는 데이터베이스가 없으면 자동으로 생성한다.

jdbc:hsqldb:hsql://localhost:9002/syaku;ifexists=true

jdbc:initialize 를 사용하여 테이블을 자동으로 생성하게 하였다.
jdbcTemplate 를 사용하기 위해 설정하였다.

@소스 src/main/resources/db-schema.sql

CREATE TABLE IF NOT EXISTS test
(
IDX INTEGER GENERATED BY DEFAULT AS IDENTITY (START WITH 1) NOT NULL,
NAME VARCHAR(250) NOT NULL,
CONTENT CLOB,
PRIMARY KEY (IDX)
);

인서트 데이터를 직접 등록하라고 컨텍스트에 등록하지 않았다.
@소스 src/main/resources/db-data.sql

INSERT INTO test (name, content) values ('spring','spring CLOB');
INSERT INTO test (name, content) values ('STS','STS CLOB');
INSERT INTO test (name, content) values ('HSQLDB','HSQLDB CLOB');

데이터 액세스용 빈을 생성한다.

package com.syaku.hsqldb;

public class TestBean {

     private Integer idx;
     private String name;
     private String content;

     public void setIdx(Integer idx) {
          this.idx = idx;
     }
     public Integer getIdx() {
          return this.idx;
     }

     public void setName(String name) {
          this.name = name;
     }
     public String getName() {
          return this.name;
     }

     public void setContent(String content) {
          this.content = content;
     }
     public String getContent() {
          return this.content;
     }
}

추후 Hibernate 와 myBATIS 를 사용하기 위해 확장성을 고려한 DAO 인터페이스를 구현하였다.

package com.syaku.hsqldb;

import java.util.List;

public interface TestDao {

     List<TestBean> getList();
     TestBean getObject(int idx);
     void insert(TestBean testBean);
     void update(TestBean testBean);
     void delete(int idx);
}

데이터 액세스를 담당할 JdbcTemplate 용 DAO 클래스이다.

package com.syaku.hsqldb;

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

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;

@Service(value = "TestDaoJdbcTemplate")
public class TestDaoJdbcTemplate implements TestDao {

     @Autowired
     private JdbcTemplate jdbcTemplate;

     public List<TestBean> getList() {

          List<TestBean> list = jdbcTemplate.query(
          "SELECT * FROM test ORDER BY idx DESC",
          new RowMapper<TestBean>() {
               public TestBean mapRow(ResultSet rs, int rowNum) throws SQLException {
                    TestBean test = new TestBean();

                    test.setIdx(rs.getInt("idx"));
                    test.setName(rs.getString("name"));
                    test.setContent(rs.getString("content"));
                    return test;
               }
          });

          return list;
     }

     public TestBean getObject(int idx) {

          TestBean test = jdbcTemplate.queryForObject(
          "SELECT * FROM test WHERE idx = ?",
          new Object[]{idx},
          new RowMapper<TestBean>() {
               public TestBean mapRow(ResultSet rs, int rowNum) throws SQLException {
                    TestBean test = new TestBean();
                    test.setIdx(rs.getInt("idx"));
                    test.setName(rs.getString("name"));
                    test.setContent(rs.getString("content"));
                    return test;
               }
          });

          return test;
     }

     public void insert(TestBean testBean) {
          String sql = "insert into test (name, content) values (?, ?)";
          jdbcTemplate.update(sql,
          new Object[] {
               testBean.getName(),
               testBean.getContent()
          });
     }

     public void update(TestBean testBean) {
          String sql = "UPDATE test SET name = ? , content = ? WHERE idx = ?";
          jdbcTemplate.update(sql,
          new Object[] {
               testBean.getName(),
               testBean.getContent(),
               testBean.getIdx()
          });
     }

     public void delete(int idx) {

          String sql = "DELETE FROM test WHERE idx = ?";
          jdbcTemplate.update(sql,
          new Object[] { idx });

     }


}

마지막으로 데이터를 처리하기 위한 로직을 담은 컨트롤러이다.

package com.syaku.hsqldb;

import java.io.PrintWriter;
import java.util.List;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * Handles requests for the application home page.
 */
@Controller
public class HomeController {

     private static final Logger logger = LoggerFactory.getLogger(HomeController.class);

     @Resource(name = "TestDaoJdbcTemplate")
     TestDao testDao;

     @RequestMapping(value = "/", method = RequestMethod.GET)
     public String home(Model model) {

          List<TestBean> list = testDao.getList();

          logger.info("" + list.size());

          model.addAttribute("list", list);

          return "home";
     }

     @RequestMapping(value = "/{idx}", method = RequestMethod.GET)
     public String view(@PathVariable Integer idx, Model model) {

          TestBean object = new TestBean();

          if (idx > 0) {
               object = testDao.getObject(idx);
          }

          model.addAttribute("object", object);

          return "view";
     }

     @RequestMapping(value = "/post", method = RequestMethod.GET)
     public String post(@RequestParam(value="idx", defaultValue="0") int idx, Model model) {

          TestBean object = new TestBean();

          if (idx > 0) {
               object = testDao.getObject(idx);
          }

          model.addAttribute("object", object);

          return "post";
     }

     @RequestMapping(value = "/post", method = RequestMethod.POST)
     public void post_save(@ModelAttribute("testBean") TestBean testBean, HttpServletResponse response) throws Exception {
          testDao.insert(testBean);

          String error = "false";
          String message = "저장되었습니다.";

          response.setContentType("application/json");
          response.setCharacterEncoding("utf-8");

          String result = StringUtils.join(new String[] {
               " { \"response\" : {",
               " \"error\" : " , error , ", ",
               " \"message\" : \"", message , "\" ",
               "} } "
          });

          PrintWriter out = response.getWriter();
          out.print(result);
          out.flush();
          out.close();
     }

     @RequestMapping(value = "/post", method = RequestMethod.PUT)
     public void post_save(@RequestBody MultiValueMap<String, String> data, HttpServletResponse response) throws Exception {
          TestBean testBean = new TestBean();

          int idx = NumberUtils.createInteger( data.getFirst("idx") );
          String name = data.getFirst("name");
          String content = data.getFirst("content");

          testBean.setIdx(idx);
          testBean.setName(name);
          testBean.setContent(content);

          testDao.update(testBean);

          String error = "false";
          String message = "수정되었습니다.";

          response.setContentType("application/json");
          response.setCharacterEncoding("utf-8");

          String result = StringUtils.join(new String[] {
               " { \"response\" : {",
               " \"error\" : " , error , ", ",
               " \"message\" : \"", message , "\" ",
               "} } "
          });

          PrintWriter out = response.getWriter();
          out.print(result);
          out.flush();
          out.close();
     }

     @RequestMapping(value = "/post", method = RequestMethod.DELETE)
     public void post_delete(@RequestBody MultiValueMap<String, String> data, HttpServletResponse response) throws Exception {

          int idx = NumberUtils.createInteger( data.getFirst("idx") );
          testDao.delete(idx);

          String error = "false";
          String message = "삭제되었습니다.";

          response.setContentType("application/json");
          response.setCharacterEncoding("utf-8");

          String result = StringUtils.join(new String[] {
               " { \"response\" : {",
               " \"error\" : " , error , ", ",
               " \"message\" : \"", message , "\" ",
               "} } "
          });

          PrintWriter out = response.getWriter();
          out.print(result);
          out.flush();
          out.close();
     }

}

수정과 삭제 application/x-www-form-urlencoded 미디어 타입을 사용하여 ajax 로 데이터가 전송되기 때문에 MultiValueMap 을 이용하여 파라메터를 얻을 수 있다. 그리고 결과는 json 으로 출력될 수 있게 구현하였다.

각 뷰페이지를 작업하여 모든 페이지를 확인한다.

목록 출력을 담당하는 페이지이며, 접근은 / 한다. 삭제기능도 포함되어 있다.
@소스 src/main/webapps/views/home.jsp

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
     <title>Spring HSQLDB TEST BY Syaku (http://syaku.tistory.com)</title>
     <script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
     <script src="http://code.jquery.com/jquery-migrate-1.2.1.min.js"></script>
</head>
<body>

<script type="text/javascript">

function del(idx) {
          $.ajax({
               url : './post',
               data: 'idx=' + idx,
               type: 'DELETE',
               dataType : 'json'
          }).done(function(body) {
               alert(body.response.message);

               if (!body.response.error) {
                    location.reload();
               }

          });
}

</script>

<c:forEach items="${list}" var="item">
<a href="./${item.idx}">${item.idx} | ${item.name} | ${item.content}</a> <button onclick="del(${item.idx})">delete</button><br />
</c:forEach>

<a href="./post">post</a>

</body>
</html>

등록 출력을 담당하는 페이지이다.

@소스 src/main/webapps/views/post.jsp

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
     <title>Spring HSQLDB TEST BY Syaku (http://syaku.tistory.com)</title>
     <script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
     <script src="http://code.jquery.com/jquery-migrate-1.2.1.min.js"></script>
</head>
<body>

<script type="text/javascript">

var method = 'POST';
<c:if test="${object.idx > 0}">
method = 'PUT';
</c:if>

jQuery(function() {
     $("#save").click(function() {
          $.ajax({
               url : './post',
               data: $('#form :input').serialize(),
               type: method,
               dataType : 'json'
          }).done(function(body) {
               alert(body.response.message);

               if (!body.response.error) {
                    location.href = "./";
               }

          });

     });
});

</script>

<form id="form">
<input type="hidden" name="idx" id="idx" value="${object.idx}" />
<div>name : <input type="text" name="name" id="name" value="${object.name}" /></div>
<div>content : <textarea name="content" id="content" rows="10" cols="10">${object.content}</textarea></div>
</form>
<button id="save">save</button>
<a href="./">list</a>

</body>
</html>

상세보기를 출력하는 페이지이다.
@소스 src/main/webapps/views/view.jsp

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
     <title>Spring HSQLDB TEST BY Syaku (http://syaku.tistory.com)</title>
</head>
<body>

<div>name : ${object.name}</div>
<div>${object.content}</div>

<a href="./post?idx=${object.idx}">update</a>

</body>
</html>

트랜잭션 적용하고 JUnit 을 이용한 테스트

스프링에서 JUnit 를 사용하기 위해 아래와 같이 pom.xml 에 dependency를 추가한다.

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework-version}</version>
<scope>test</scope>
</dependency>

<!-- CGLib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.1</version>
<type>jar</type>
<scope>compile</scope>
</dependency>

트랜잭션을 사용하기 위해 아래와 같이 설정을 수정한다.

@소스 src/main/resources/config-context.xml

<?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:util="http://www.springframework.org/schema/util"
     xmlns:context="http://www.springframework.org/schema/context"
     xmlns:p="http://www.springframework.org/schema/p"
     xmlns:jdbc="http://www.springframework.org/schema/jdbc"
     xmlns:tx="http://www.springframework.org/schema/tx"
     xmlns:aop="http://www.springframework.org/schema/aop"
     xsi:schemaLocation="
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
     http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
     http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
     http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
     ">

     <!-- HSQLDB STARTER
     <bean id="hsqldb" class="com.syaku.hsqldb.HSQLDB" init-method="start">
          <constructor-arg>
               <value>
               server.database.0 = file:/Users/syaku/develop/spring/hsqldb/syaku
               server.dbname.0 = syaku
               server.remote_open = true
               server.port = 9002
               hsqldb.reconfig_logging = false
               </value>
          </constructor-arg>
     </bean>-->

     <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
          <property name="driverClassName" value="org.hsqldb.jdbcDriver" />
          <property name="url" value="jdbc:hsqldb:hsql://localhost:9002/syaku" />
          <property name="username" value="sa" />
          <property name="password" value="" />
     </bean>

     <!-- CREATE TABLE -->
     <jdbc:initialize-database data-source="dataSource" ignore-failures="DROPS">
     <jdbc:script location="classpath:db-schema.sql" />
     </jdbc:initialize-database>

     <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
          <property name="dataSource" ref="dataSource"/>
     </bean>

     <!-- Transaction Manager -->

     <context:component-scan base-package="com.syaku.hsqldb" use-default-filters="false">
     <context:include-filter expression="org.springframework.stereotype.Service" type="annotation" />
     </context:component-scan>

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

     <tx:advice id="txAdvice" transaction-manager="transactionManager">
     <tx:attributes>
     <tx:method name="get*" read-only="true" />
     <tx:method name="insert*" />
     <tx:method name="update*" />
     <tx:method name="delete*" />
     </tx:attributes>
     </tx:advice>

     <aop:config>
     <aop:pointcut id="transactionPointcut" expression="execution(* com.syaku.hsqldb.TestDao*.*(..))"/>
     <aop:advisor advice-ref="txAdvice" pointcut-ref="transactionPointcut" />
     </aop:config>

</beans>

그리고 서블릿 컨텍스트의 컴포넌트 스캔을 수정한다.

소스 src/main/webapp/spring/appServlet/servlet-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:beans="http://www.springframework.org/schema/beans"
     xmlns:context="http://www.springframework.org/schema/context"
     xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
          http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
          http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

     <!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->

     <!-- Enables the Spring MVC @Controller programming model -->
     <annotation-driven />

     <!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
     <resources mapping="/resources/**" location="/resources/" />

     <!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
     <beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
          <beans:property name="prefix" value="/WEB-INF/views/" />
          <beans:property name="suffix" value=".jsp" />
     </beans:bean>

     <!-- <context:component-scan base-package="com.syaku.hsqldb" /> -->
     <context:component-scan base-package="com.syaku.hsqldb" use-default-filters="false">
     <context:include-filter expression="org.springframework.stereotype.Controller" type="annotation" />
     </context:component-scan>

</beans:beans>

테스트를 위한 클래스를 생성한다.

@소스 src/test/java/com/syaku/hsqldb/TestDaoTest.java

package com.syaku.hsqldb;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:config-context.xml")
public class TestDaoTest {

     @Resource(name = "TestDaoJdbcTemplate")
     TestDao testDao;

     @Test(expected=RuntimeException.class)
     @Transactional
     public void data() {

          TestBean testBean = new TestBean();
          testBean.setName("test junit!!!");
          testBean.setContent("test content");
          testDao.insert(testBean);

          testBean.setName("test junit!!!");
          testBean.setContent("test content");
          testDao.insert(testBean);

          testBean.setName("ㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁtest junit!!!");
          testBean.setContent("sss");
          testDao.insert(testBean);
     }
}

좀 무식한 방법이지만;;; 기존에 작성한 포스팅 내용을 수정하지 않기위해~~ 250자를 넘는 name 값을 넣어 SQL 익셉션이 발생하게 하였다!!!
런타임익셉션이 나면 테스트가 성공으로 표시된다.

그런데… 선언적인 트래잭션만 지정했지~ 어노테이션 트랜잭션은 선언한 적이 없는 데… @Transactional 작동한다… ;;;

참고자료

스프링 HSQLDB 예제 http://devcrumb.com/hibernate/hibernate-jpa-spring-and-hsqldb
스프링 HSQLDB 예제 http://blog.beany.co.kr/archives/1187
HSQLDB 서버 구동 클래스 http://www.javacodegeeks.com/2012/11/embedding-hsqldb-server-instance-in-spring.html
JUnit http://using.tistory.com/54

Spring JdbcTemplate http://docs.spring.io/spring/docs/4.0.x/spring-framework-reference/html/jdbc.html#jdbc-SimpleJdbcTemplate

Test JUnit Assert Method http://junit.sourceforge.net/javadoc/org/junit/Assert.html
Thank you!!!

posted syaku blog

Syaku Blog by Seok Kyun. Choi. 최석균.

http://syaku.tistory.com