Full Stack Web Developer.
Syaku (샤쿠)

Java, JS (ES6+), Spring, Spring security, jQuery, Reactjs, Bootstrap.

        

07-03 03:38


Spring Boot 2 보기 Front-end 보기 DevOps 보기 Spring 3 보기 Spring Security 3 보기

#3 스프링 MyBatis 설정하기 및 로그출력 - 스프링 프레임워크 게시판 : Spring Framework MyBatis Log4jdbc

written by Seok Kyun. Choi. 최석균


스프링 프레임워크 연재 포스팅

2014/07/21 - [개발노트/Spring] - 스프링 프레임워크 게시판 #1 STS 설치 및 스프링 프로젝트 만들기 : Spring Framework Hello, World!!!
2014/07/21 - [개발노트/Spring] - 스프링 프레임워크 게시판 #2 스프링 프로젝트 만들기 : Spring Framework Create Project
2014/07/21 - [개발노트/Spring] - 스프링 프레임워크 게시판 #3 스프링 MyBatis 설정하기 및 로그출력 : Spring Framework MyBatis Log4jdbc
2014/07/21 - [개발노트/Spring] - 스프링 프레임워크 게시판 #4 스프링 XML , 스프링 유효성검사 : Spring Framework Hibernate Validator XML Marshaller
2014/07/21 - [개발노트/Spring] - 스프링 프레임워크 게시판 #5 스프링 트랜잭션 : Spring Framework Transaction
2014/07/21 - [개발노트/Spring] - 스프링 프레임워크 게시판 #6 스프링 파일업로드 : Spring Framework FileUpload
2014/07/28 - [개발노트/Spring] - 스프링 프레임워크 게시판 #부록 스프링 검색 및 조회수 올리기 , 스프링 한글깨짐: Spring Framework Cookie


개발 환경

Mac OS X 10.9.4
JAVA 1.6
Apache Tomcat 7.x
MySQL 5.x
Spring 3.1.1
Spring Tool Suite 3.5.1
Maven 2.5.1
myBatis 3.2.7
jQuery 1.11.0

2014.07.19 Written by 최석균 (Syaku)

소스파일 :  source-3.zip

3. myBatis 와 Spring 연동하기

게시판 프로젝트에 사용되는 RDBMS 는 MySQL 5.x 이상을 사용하고 언어셋은 utf-8 이다.

myBatis 는 두가지 버전이 있다. myBatis 2.x 버전은 원래 iBatis 라 불렸고, 현재까지 배포되고 있는 myBatis 3.x 버전이 myBatis 이다. 두버전은 사용법이 전혀 다르므로 혼동이 없어야 한다. 그리고 iBatis 즉 2.x 버전은 패치작업이 중단되었고, 3.x 버전만 패치를 지원해고 있다.

myBatis 와 MySQL 을 사용하기 위해 아래의 라이브러리를 설치해야한다.

  • spring-jdbc : Spring 에서 지원하는 JDBC
  • mysql-connector-java : MySQL 커넥션 드라이브를 제공
  • mybatis : myBatis
  • mybatis-spring : Spring 에서 연동을 지원하는 myBatis
  • commons-dbcp : 커넥션풀을 담당하는 Apache Commons DBCP
  • commons-lang : myBatis 연동과 관련은 없지만, 빈번하게 사용되는 문자열 라이브러리를 Aapche Commons Lang 로 통일함.

스프링에 라이브러리를 설치하려면 pom.xml 에 dependency 를 추가하면 된다.
위 라이브러리들을 Maven Repository에서 검색 후 적당한 버전의 dependency 를 복사해서 아래와 같이 등록한다.

spring-* 스프링 라이브러리는 spring 버전과 동일한 라이브러리를 설치해야 한다. 그래서 version 노드에 ${org.springframework-version} 삽입하면 된다.

@소스 pom.xml

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

<!-- mysql-connector-java -->
<dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
   <version>5.1.31</version>
</dependency>

<!-- mybatis -->
<dependency>
   <groupId>org.mybatis</groupId>
   <artifactId>mybatis</artifactId>
   <version>3.2.7</version>
</dependency>
<dependency>
   <groupId>org.mybatis</groupId>
   <artifactId>mybatis-spring</artifactId>
   <version>1.2.2</version>
</dependency>

<!-- commons-dbcp -->
<dependency>
   <groupId>commons-dbcp</groupId>
   <artifactId>commons-dbcp</artifactId>
   <version>1.4</version>
</dependency>

<!-- commons-lang -->
<dependency>
   <groupId>commons-lang</groupId>
   <artifactId>commons-lang</artifactId>
   <version>2.6</version>
</dependency>

자동으로 Maven 이 빌드 된다. 만약 안될 경우 수동으로 빌드한다.
빌드가 완료되면 설치된 라이브러리는 Maven Dependencies 폴더에서 확인할 수 있다.

DB Connection 과 myBatis 설정파일 만들기

설정 파일들은 resource 폴더에 생성한다.

MySQL 접속 정보 설정 프로퍼티를 src/main/resource 경로 아래 생성한다.

@소스 jdbc.properties

jdbc.driver = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/spring?useUnicode=yes&amp;characterEncoding=UTF8&amp;autoReconnect=true&amp;autoReconnectForPools=true
jdbc.username = spring
jdbc.password = spring

RDBMS 드라이브, 접속경로, 계정, 암호 순으로 항목을 입력한다.

다음은 스프링에서 MySQL 에 접속하는 설정 파일을 생성한다.

resource 폴더에는 다양한 정보를 저장히기 때문에 폴더를 구분하여 관리하고 resource 폴더 아래 config/spring/context 폴더(패킷)를 생성하고 아래의 파일도 생성한다.

@소스 context-datasource.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">

<bean id="jdbcProp" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:jdbc.properties" />
</bean>

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

</beans>

jdbcProp 는 jdbc.properties 파일을 읽어온다.
dataSource 는 Apache Commons DBCP 를 이용하여 MySQL과 연결을 진행한다.
jdbcProp 에서 읽어온 프로퍼티 변수들은 ${jdbc.driver} 형식으로 사용할 수 있다.

디비 접속 담당은 스프링에서 Apache Commons DBCP 가 하며, myBatis 는 맵퍼를 통한 쿼리를 실행하고 값들을 주고 받는 역활을 담당한다.

@소스 mybatis-config-base.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>

    <settings>  
        <setting name="cacheEnabled"              value="true"/>  
        <setting name="lazyLoadingEnabled"        value="false"/>  
        <setting name="multipleResultSetsEnabled" value="true"/>  
        <setting name="useColumnLabel"            value="true"/>  
        <setting name="useGeneratedKeys"          value="false"/>  
        <setting name="defaultExecutorType"      value="SIMPLE"/>  
        <setting name="defaultStatementTimeout"  value="25000"/>  
    </settings>

    <typeHandlers>
        <!--  java.sql.Timestamp 를 java.util.Date 형으로 반환 -->
        <typeHandler javaType="java.sql.Timestamp" handler="org.apache.ibatis.type.DateTypeHandler"/>
        <typeHandler javaType="java.sql.Time"      handler="org.apache.ibatis.type.DateTypeHandler"/>
        <typeHandler javaType="java.sql.Date"      handler="org.apache.ibatis.type.DateTypeHandler"/>
    </typeHandlers>

</configuration>

위 소스는 myBatis 설정 정보의 파일이다. 필요한 옵션을 추가하면 된다.
옵션에 대한 상세한 설명은 mybatis.org 튜토리얼에서 참조할 수 있다.

다음은 스프링과 myBatis 연동하는 설정 파일이다.

@소스 context-mybatis.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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="configLocation" value="classpath:config/spring/context/mybatis-config-base.xml" />
        <property name="mapperLocations">
            <list>
                <value>classpath*:com/syaku/**/dao/*Mapper.xml</value>
            </list>
        </property>
    </bean>

    <!-- scan for mappers and let them be autowired -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.syaku.bbs" />
        <property name="annotationClass" value="org.springframework.stereotype.Repository"/>
    </bean>
</beans>

이젠 설정파일을 하위 컨텍스트에서 사용할 수 있게 root-context.xml 에 impoert 시킨다.
root-context.xml 파일의 위치는 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/spring/context/context-datasource.xml"/>
    <import resource="classpath:config/spring/context/context-mybatis.xml" />
</beans>


스프링 프레임워크는 아주 간결하게 myBatis 와 연동을 지원한다.
원래 myBatis 사용하기 위해 맵퍼들을 직접 호출해야 하지만 스프링에서는 스캔을 통해 자동적으로 맵퍼를 호출할 수 있다.
프로퍼티 mapperLocations 노드에서 스캔하려는 맵퍼의 경로 범위를 설정하면 경로와 일치하는 맵퍼를 모두 호출하게 된다. 그리고 맵퍼의 인터페이스를 자동으로 스캔하는 기능도 제공하고 있다.

맵퍼의 인터페이스를 생성하여 사용하는 것은 필수 조건은 아니다. 다만 myBatis 에서 사용하는 것을 권장한다.
주어진 SQL 구문의 파라미터와 리턴값을 설명하는 인터페이스(예를 들면, BlogMapper.class )를 사용하여, 문자열 처리 오류나 타입 캐스팅 오류 없이 좀더 타입에 안전하고 깔끔하게 실행할 수 있기때문이라고 한다.
좀 더 자세한 사항은 myBatis 메뉴얼에서 SqlSessionFactory 에서 SqlSession 만들기 섹션을 참조하면 된다.

여기까지 스프링과 myBatis 연동을 위한 설정 작업은 모두 마쳤다. 다음은 데이터를 조회하고 출력하는 작업을 하기로 한다.

게시판 테이블 생성하고, 테이블 항목(필드)에 맞는 VO 를 생성한다.
VO(Value Object) 는 DB 데이터를 조회 후 결과를 담는 객체를 말한다.

@소스 게시판 테이블 생성 쿼리

CREATE TABLE `bbs` (
  `idx` bigint(20) NOT NULL AUTO_INCREMENT,
  `user_name` varchar(50) DEFAULT NULL,
  `subject` varchar(250) DEFAULT NULL,
  `content` longtext,
  `reg_datetime` char(14) DEFAULT NULL,
  PRIMARY KEY (`idx`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8

VO를 생성하기 위해 src/main/java 에 아래의 패킷을 생성한다.

com.syaku.bbs.dao

@소스 BbsVo.java

package com.syaku.bbs.dao;
public class BbsVo {
    private Integer idx;
    private String user_name;
    private String subject;
    private String content;
    private String reg_datetime;

    public Integer getIdx() {
        return idx;
    }
    public void setIdx(Integer idx) {
        this.idx = idx;
    }
    public String getUser_name() {
        return user_name;
    }
    public void setUser_name(String user_name) {
        this.user_name = user_name;
    }
    public String getSubject() {
        return subject;
    }
    public void setSubject(String subject) {
        this.subject = subject;
    }
    public String getContent() {
        return content;
    }
    public void setContent(String content) {
        this.content = content;
    }
    public String getReg_datetime() {
        return reg_datetime;
    }
    public void setReg_datetime(String reg_datetime) {
        this.reg_datetime = reg_datetime;
    }
}

다음은 myBatis 에서 CRUD 를 작업 할 맵퍼를 생성한다.
CRUD 란 Create Read Update Delete 의 작업을 말한다.

@소스 BbsMapper.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="com.syaku.bbs.dao.BbsMapper">

    <resultMap id="bbsMap" type="com.syaku.bbs.dao.BbsVo">
        <id     property="idx"    column="idx" />
        <result property="user_name" column="user_name" />
        <result property="subject"       column="subject" />
        <result property="content"       column="content" />
        <result property="reg_datetime"       column="reg_datetime" />
    </resultMap>

    <select id="select" resultMap="bbsMap">
        SELECT  * FROM bbs
    </select>

    <select id="selectOne" resultMap="bbsMap">
        SELECT  * FROM bbs WHERE idx = #{idx}
    </select>

    <insert id="insert">
         INSERT INTO bbs ( user_name, subject, content, reg_datetime ) VALUES (
                #{user_name}
              , #{subject}
              , #{content}
              , #{reg_datetime}
         )
    </insert>

    <update id="update">
         UPDATE bbs SET
              user_name = #{user_name},
              subject = #{subject},
              content = #{content},
              reg_datetime = #{reg_datetime}
         WHERE idx = #{idx} LIMIT 1
    </update>

    <delete id="delete">
         DELETE FROM bbs WHERE idx = #{idx}
    </delete>

</mapper>

맵핑 인터페이스를 사용하기 때문에 parameterType 사용하지 않아도 된다.

다음은 myBatis 맵퍼 인터페이스를 생성한다.

@소스 BbsMapper.java

package com.syaku.bbs.dao;

import java.util.List;
import org.springframework.stereotype.Repository;

@Repository(value = "bbsMapper")
public interface BbsMapper {
    List<BbsVo> select();

    BbsVo selectOne(int idx);
    void insert(BbsVo bbsVo);
    void update(BbsVo bbsVo);
    void delete(int idx);
}

context-mybatis.xml 에 Repository 어노테이션을 자동으로 스캔하도록 작업을 하였고, 맵퍼 인터페이스에 @Repository 어노테이션을 정의하였다.

다음은 VO 와 Mapper 를 연결해주는 DAO를 생성한다.

@소스 BbsDao.java

package com.syaku.bbs.dao;

import java.util.List;

import javax.annotation.Resource;
import org.springframework.stereotype.Service;

@Service(value = "bbsDao")
public class BbsDao {
    @Resource(name = "bbsMapper")
    private BbsMapper bbsMapper;

    public List<BbsVo> getSelect() {
        return this.bbsMapper.select();
    }

    public BbsVo getSelectOne(int idx) {
        return this.bbsMapper.selectOne(idx);
    }

    public void insert(BbsVo bbsVo) {
         this.bbsMapper.insert(bbsVo);
    }

    public void update(BbsVo bbsVo) {
         this.bbsMapper.update(bbsVo);
    }

    public void delete(int idx) {
         this.bbsMapper.delete(idx);
    }
}

Service 어노테이션을 지정하였다.
현재 myBatis 는 규격화된 프로그래밍이 가능하도록 제약사항이 많아졌다. 그래서 iBatis 보다 복잡해진 것이다. 불편하다면 iBatis 를 사용해도 무방하다. 단 더이상 iBatis 버전업은 지원하지 않는 다.


게시판 DB 데이터 적용하기

현재 만들어 놓은 정적인 게시판을 DB와 연동하여 동적인 게시판으로 업데이트하는 작업을 진행한다.
ViewController 에 myBatis CRUD 작업을 할 수 있게 소스를 업데이트한다.

@소스 ViewController.java

package com.syaku.bbs;

import java.util.List;
import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import com.syaku.bbs.dao.*;

@Controller(value = "viewController")
public class ViewController {
    private static final Logger logger = LoggerFactory.getLogger(ViewController.class);

    // Resource 어노테이션을 이용하여 BbsDao 선언.
    @Resource(name = "bbsDao")
    private BbsDao bbsDao;

    // 게시판 목록
    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String dispBbsList(Model model) {
        logger.info("display view BBS list");
        List<BbsVo> list = this.bbsDao.getSelect();
        model.addAttribute("list", list);

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

        return "bbs.list";
    }

    // 게시판 상세보
    // PathVariable 어노테이션을 이용하여 RESTful 방식 적용
    // bbs/1 -> id = 1; id = 게시물 번호로 인식함.
    // 일반 적으로 (@ReuqstParam(value = "bbsVo", required = false, defaultValue = "0"), int idx, Model model)
    @RequestMapping("/{idx}")
    public String dispBbsView(@PathVariable int idx, Model model) {
        logger.info("display view BBS view idx = {}", idx);
        BbsVo object = this.bbsDao.getSelectOne(idx);

        model.addAttribute("object", object);
        return "bbs.view";
    }

    // 게시판 쓰기
    @RequestMapping(value = "/write", method = RequestMethod.GET)
    public String dispBbsWrite(@RequestParam(value="idx", defaultValue="0") int idx, Model model) {
        logger.info("display view BBS write");

        if (idx > 0) {
            BbsVo object = this.bbsDao.getSelectOne(idx);
            model.addAttribute("object", object);
        }

        return "bbs.write";
    }

    @RequestMapping(value = "/write_ok", method = RequestMethod.POST)
    public String procBbsWrite(@ModelAttribute("bbsVo") BbsVo bbsVo, RedirectAttributes redirectAttributes) {
        Integer idx = bbsVo.getIdx();

        if (idx == null || idx == 0) {
            this.bbsDao.insert(bbsVo);
            redirectAttributes.addFlashAttribute("message", "추가되었습니다.");
            return "redirect:/";
        } else {
            this.bbsDao.update(bbsVo);
            redirectAttributes.addFlashAttribute("message", "수정되었습니다.");
            return "redirect:/write?idx=" + idx;
        }
    }

    @RequestMapping(value = "/delete", method = RequestMethod.POST)
    public String procBbsDelete(@RequestParam(value = "idx", required = false) int idx) {
        this.bbsDao.delete(idx);
        return "redirect:/";
    }

}

데이터를 저장하고 갱신하는 비지니스 로직 2개가 추가되었다.
procBbsWrite 는 글을 추가하고 수정하는 역활을 하고, procBbsDelete 는 글을 삭제하는 역활을 한다.
글 번호를 Integer 타입으로 사용한 이유는 글을 추가할 경우 기본적으로 글 번호가 없기 때문에 NULL 을 받기 위해서이다. null 이거나 0 인 경우 글 추가 작업을 하고 아닌 경우 수정을 하게된다.

각 상수에 사용한 어노테이션은 다음과 같다.
@RequestParameter 는 요청된 파라메터의 값을 찾아 변수에 담아주는 역활을 한다. defaultValue 는 값이 empty 인 경우 치환하는 작업을 한다.
defaultValue 를 지정하지 않을 경우 파라메터 값은 필수조건이기 때문에 익셉션이 발생한다.
@ModelAttribute 는 파라메터 값을 Vo 에 자동으록 맵핑하는 작업을 한다.
RedirectAttributes 는 작업을 처리 후 리다이렉트 된 화면에 변수 값을 넘겨주는 역활을 한다.

뷰페이지에 데이터를 출력하는 작업을 한다.

@소스 bbs.list.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="utf-8">
    <title>스프링프레임워크 게시판</title>
  </head>
  <body>
  <h1>${message}</h1>
  <table border="1">
    <colgroup>
  <col width="60"><col><col width="115"><col width="85">
  </colgroup>
  <thead>
    <tr>
      <th scope="col">번호</th>
      <th scope="col">제목</th>
      <th scope="col">작성자</th>
      <th scope="col">등록일</th>
    </tr>
    </thead>

    <tbody>
    <!-- 목록이 반복될 영역 -->
    <c:forEach var="item" items="${list}" varStatus="status">
    <tr>
      <td>${item.idx}</td>
      <td><a href="./${item.idx}">${item.subject}</a></td>
      <td>${item.user_name}</td>
      <td>${item.reg_datetime}</td>
    </tr>
    </c:forEach>

    </tbody>

  </table>
  <div><a href="./write">쓰기</a></div>
  </body>
</html>

@소스 bbs.view.jsp

<%@ page contentType="text/html; charset=utf-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="utf-8">
    <title>스프링프레임워크 게시판</title>
  </head>
  <body>

    <script>
    function del(){
      if (confirm("삭제하시겠습니까?")) document.form.submit();
    }
    </script>

    <form id="form" name="form" method="post" action="./delete">
      <input type="hidden" id="idx" name="idx" value="${object.idx}" />
    </form>
    <p>${object.subject}</p>

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

    <div>
    <p>작성자 : ${object.user_name}</p>
    <p>등록일 : ${object.reg_datetime}</p>
    </div>

    <div>
      <button type="button" onclick="del()">삭제</button>
      <a href="./write?idx=${object.idx}">수정</a>
      <a href="./">목록</a>
    </div>
  </body>
</html>

@소스 bbs.write.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="utf-8">
    <title>스프링프레임워크 게시판</title>
  </head>
  <body>
  <h1>${message}</h1>
  <form id="form" method="post" action="./write_ok">
  <input type="hidden" name="idx" id="idx" value="${object.idx}" />
  <div>
  <span>제목</span>
  <input type="text" id="subject" name="subject" value="${object.subject}" />
  </div>
  <div>
  <span>작성자</span>
  <input type="text" id="user_name" name="user_name" value="${object.user_name}" />
  </div>
  <div>
  <span>내용</span>
  <textarea id="content" name="content" rows="10" cols="20">${object.content}</textarea>
  </div>

  <div>
  <input type="submit" value="save" />
  <a href="./">목록</a>
  </div>
  </form>
  </body>
</html>

게시판에 글을 등록해서 데이터를 확인한다.

한글이 깨져서 저장되었다. 이럴경우 아래의 소스를 web.xml에 추가한다.

@소스 web.xml

<!-- utf-8 filter -->
<filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
</filter>

<filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

다시 게시판을 등록해보고 한글이 깨지는 지 확인한다.


MyBatis 로그 출력하기

로그 출력을 위해 Maven 에 log4jdbc 라이브러리를 추가한다.

@소스 pom.xml

<!-- jdbc log -->
<dependency>
    <groupId>org.lazyluke</groupId>
    <artifactId>log4jdbc-remix</artifactId>
    <version>0.2.7</version>
</dependency>

그리고 로그 출력을 위한 설정 정보를 추가한다.

@소스 context-datasource.xml

<bean id="dataSource" class="net.sf.log4jdbc.Log4jdbcProxyDataSource">
 <constructor-arg ref="dataSourceSpied" />
 <property name="logFormatter">
  <bean class="net.sf.log4jdbc.tools.Log4JdbcCustomFormatter">
   <property name="loggingType" value="MULTI_LINE" />
   <property name="sqlPrefix" value="SQL:::" />
  </bean>
 </property>
</bean>

기존에 dataSource 를 dataSourceSpied 로 변경하고 위 소스를 추가하면 된다.
마지막으로 로그 출력방식을 설정하기 위해 정보를 추가한다.
/src/main/resources/log4j.xml 파일을 열어 아래와 같이 수정한다.

@소스 log4j.xml

<!-- 
# log4jdbc settings development production
jdbc.connection     :     WARN       WARN
jdbc.audit          :     WARN       WARN
jdbc.sqlonly        :     WARN       WARN
jdbc.sqltiming      :     INFO       WARN
jdbc.resultset      :     WARN       WARN
jdbc.resultsettable :     INFO       WARN
-->

<logger name="jdbc.connection" additivity="false">
    <level value="WARN"/>
    <appender-ref ref="console"/>
</logger>

<logger name="jdbc.audit" additivity="false">
    <level value="WARN"/>
    <appender-ref ref="console"/>
</logger>

<logger name="jdbc.sqlonly" additivity="false">
    <level value="WARN"/>
    <appender-ref ref="console"/>
</logger>

<logger name="jdbc.sqltiming" additivity="false">
    <level value="INFO"/>
    <appender-ref ref="console"/>
</logger>

<logger name="jdbc.resultset" additivity="false">
    <level value="WARN"/>
    <appender-ref ref="console"/>
</logger>

<logger name="jdbc.resultsettable" additivity="false">
    <level value="INFO"/>
    <appender-ref ref="console"/>
</logger>

STS 콘솔 정보를 확인하면 아래와 같은 로그가 출력된다.




posted syaku blog

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

http://syaku.tistory.com


  • 이전 댓글 더보기
  • 손님 at 2015.05.22 08:23

    글을 정말 깔끔하게 잘 작성하셨네요. 디비로그 출력을 어떻게 하나 궁금해서 접속하게 되었는데, 도움될만한 다른 글들도 많은 것같네요.. 잘보고 갑니다.

  • 더하나 at 2015.06.05 17:42

    포스트 항상 감사하며 잘 보고 있습니다. 꾸벅.
    이번 포스트의 마지막 로그출력을 위해서 log4j.xml을 수정하였더니,
    XML 문제가 발생하였습니다.
    The content of elements type "log4j:configuration" must match "(renderer*,appender*,plugin*,(category|logger)*,root?,(categoryFactory|loggerFactory)?)"
    위치는 파일 위에서 3번째줄인 <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">입니다.
    참고로 log4j.xml은 위의 수정할 내용을 <!-- Root Logger --> 항목 밑에 붙였습니다.

  • 스프링초보 at 2015.06.13 19:26

    안녕하세요, 블로그 게시글 잘 보고있습니다!!
    질문이 하나 있어서 질문드리겠습니다.
    토비 스프링 보면서 같이 공부하고있는데,, 포스팅 하신 글에 BbsMapper 인터페이스에 @Repository 어노테이션을 붙이시고,
    BbsDao에 @Service 어노테이션을 사용하셨는데,
    제가 잘못알 수도있지만 알고있는건, @Service는 서비스 계층 빈을 구분하기위해쓰고, @Repository는 Dao를 구현한 class에 사용한다고 알고있는데..
    어노테이션이 저렇게 사용하는게 맞는건가요..?? 맞다면 왜 저렇게 사용이 된건지좀 알려주세요..

    그리고 한가지 질문이 더있습니다..
    BbsMapper는 단순히 xml파일과 매핑을 위해서 사용하는것인가요?? 아니면 Dao의 인터페이스로도 사용되는건가요??

    만약, Dao를 클래스로 사용하지 않고, Dao에 대한 인터페이스를 따로 두고, Dao인터페이스를 구현한 클래스를 사용하려면 BbsMapper와 별도로 Dao에대한 인터페이스를 따로 구현해야하나요???

    질문이 너무 두서가 없었는데.. 답변 부탁드려요!! 감사합니다!

    • 샤쿠 syaku at 2015.06.14 22:34 신고

      네 맞습니다. 비지니스로직이 있는 작업을 서비스계층으로 지정하시면 됩니다.

      BbsMapper 은 오직 myBatis 를 위한 인터페이스입니다.
      BbsDao 는 Repository 계층에서 확장성을 위한 인터페이스입니다.

      여기서 확장성이라하면.. myBatis 가 아닌 하이비네이트나 jdbc템플릿으로 사용할 수 있게하는 것을 의미합니다.

      마지막으로 꼭 Dao가 필요한 것은 아닙니다. BbsMapper 만으로도 충분히 서비스에서 di해서 사용할 수 있습니다.

  • 스프링초보 at 2015.06.17 01:18

    윗 답변 갑사드립니다.
    죄송한데 한가지 질문 더 드려도될까요??

    댓글들 중에 맨 위에서 4번째 쯤에 답변하신 내용 중에,

    [ service 계층은 dao 의 역활을 좀 더 확장하여 비지니스 로직을 담당하는 계층입니다... 그런데 위 내용상 비지니스로직이라고는 없어.. dao 패턴에 서비스계층을 적용한 것입니다. ]

    라고 말씀하신 부분이 있는데요,

    제가 만약에, Service와 DAO, Mapper를 따로 두고 사용을 한다면, @service 어노테이션을 Service에 붙여주고
    Mapper에만 @Repository 어노테이션을 붙이면 되나요?? 아니면 DAO에도 @Repository를 붙여야 하나요??

    • 샤쿠 syaku at 2015.06.17 09:20 신고

      Service = @Service
      Mapper = @Repository
      DAO = 없음

      @Service
      class service {
      @Resource(name = "Mapper") DAO dao;
      }


      @Repository
      interface Mapper extends DAO {
      }

      interface DAO {
      .. TODO ..
      }

      위처럼하시면 됩니다.

  • 스프링어려웡 at 2015.06.21 21:06

    안녕하세요! 사쿠님!
    자료를 다 실행시켜보고 성공했는데
    혹시 테이블을 2개 넣고 싶으면 어떻게하는걸까요? BbsVo나 BbsMapper들을 2개씩 생성해줘야하나요??

    • 샤쿠 syaku at 2015.06.22 11:24 신고

      2개면 각각의 VO 와 Mapper (DAO 가 있을 경우 생성해야함)
      그렇게 만들어 진 것들은 이미 생성해 놓은 Service 에서 di 하여 사용하면 됩니다.

  • 스프링어려웡 at 2015.06.23 22:20

    안녕하세요! 샤쿠님! 지난 답변 감사합니다!
    vo 와 mapper을 2개씩 만들고 (bbsvo, bbsvo3 이렇게) 실행해봤는데
    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String dispBbsList(Model model) {
    logger.info("display view A list");
    List<BbsVo> list = bbsDao.getSelect();
    model.addAttribute("list", list);
    logger.info("totcal count" + list.size());
    return "bbs.list";
    }

    @RequestMapping(value = "/3", method = RequestMethod.GET)
    public String dispBbsList3(Model model3) {
    logger.info("display view B list");
    List<BbsVo3> list3 = bbsDao3.getSelect();
    model3.addAttribute("list3", list3);
    logger.info("totcal count" + list3.size());
    return "bbs.list3";
    }
    를 viewcontroller 에 넣었습니다.
    그런데 위에 value = "/" 인것은 결과 화면이 나오는데
    value = "/3" 값은 결과가 나오지 않습니다.. value 값을 무엇으로 설정해줘야 나올까요??
    value 값이 겹치면 실행이 되지않고 처음 dispBbsList에 if-else문을 써서 return 값을 다르게 출력해봤지만 화면이 나오지 않아서 문의드립니다 :)

  • 배워봅니다 at 2015.07.20 00:24

    샤쿠님 mybatis 오라클 연동하는거도 갈쳐주세요 ㅠ

    • 샤쿠 syaku at 2015.07.20 09:31 신고

      pom.xml 에 아래와 같이 추가합니다.

      <repositories>

      ... 생략

      <repository>
      <id>mesir-repo</id>
      <url>http://mesir.googlecode.com/svn/trunk/mavenrepo</url>
      </repository>

      ... 생략

      </repositories>



      <dependency>
      <groupId>com.oracle</groupId>
      <artifactId>ojdbc14</artifactId>
      <version>10.2.0.4.0</version>
      </dependency>

      그리고 datasource 설정은 아래아 같이~

      driverClassName=oracle.jdbc.driver.OracleDriver
      url=jdbc:oracle:thin:@localhost:1521:ORCL
      username=
      password=

  • 스프링 at 2015.07.30 15:13

    등록일은 왜 안보이는건가요??

    • 샤쿠 syaku at 2015.07.30 16:05 신고

      급했는 지... 등록일을 기록하지 않았네요~

      게시글 등록할때 날짜를 등록해주시면 될 것 같네요~
      한번 해보세요~

      테이블 생성할때 날짜를 date 타입으로 하시는 게 좋을거에요~
      (내용에는 캐릭터로 되어있음)
      저장할때는 bbsVO.setReg_datetime( new Date() ); 하면 됩니다~

    • at 2016.03.16 20:33

      비밀댓글입니다

    • 샤쿠 syaku at 2016.03.17 14:41 신고

      bbsDAO 에 insert 에 넣으시면 됩니다... 저 당시에는 서비스를 따로 만들지 않았네요~

    • at 2016.03.21 11:28

      비밀댓글입니다

    • 샤쿠 syaku at 2016.03.21 11:33 신고

      네 bbsDAO.java 클래스에 보면 insert 메서드가 있을거에요
      bbsVO에 넣으시면 됩니다.. 데이터 타입을 문자열로 해놨네요~ Date 타입으로 바꾸셔도 되고 new Date 를 문자열포맷으로 얻어 넣으시면 됩니다.

    • at 2016.03.21 11:47

      비밀댓글입니다

    • 샤쿠 syaku at 2016.03.21 13:11 신고

      위에 댓글처럼 reg_datetime 이 날짜 형식이 아니라 문자열입니다.

      Date date = new Date();
      SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss");
      String reg_date = formatter.parse(date);
      bbsVo.setReg_datetime(reg_date);

      하시면 됩니다.

    • at 2016.03.21 14:53

      비밀댓글입니다

    • 샤쿠 syaku at 2016.03.21 14:54 신고

      변수를 먼저 삽입해주시고 인서트를 해야되겠지요~
      순서가 잘못되었습니다. ^^;;

      Date date = new Date();
      SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss");
      String reg_date = formatter.format(date);
      bbsVo.setReg_datetime(reg_date);

      this.bbsMapper.insert(bbsVo);

      프로그램은 위에서 부터 아래로 순차적으로 처리되니까용~

    • at 2016.03.21 15:03

      비밀댓글입니다

    • at 2016.03.23 10:52

      비밀댓글입니다

    • 공부하고싶어 at 2016.07.13 09:48

      Date date = new Date();
      SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss");
      String reg_date = formatter.parse(date);
      bbsVo.setReg_datetime(reg_date);

      여기서
      String reg_date = formatter.parse(date);
      parse부분에 빨간줄이떠서 에러를 확인해보니
      Date date = new Date(); 에서
      String date = new Date();로 바꿔야된다고 나오더라구요
      근데 바꿔도 다른 에러가 더 발생해가지구..
      도대체 뭐가 문제인지 못찾고있습니다 .

    • 샤쿠 syaku at 2016.07.13 09:56 신고

      죄송합니다... 제가 오류를 발생시켰네요~

      Date date = new Date();
      SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss");
      String reg_date = formatter.format(date);

      parse 가 아니라 format 입니다.

    • 공부하고싶어 at 2016.07.13 10:02

      빠른 답변 정말감사드립니다 ^^
      제가
      mysql에서
      reg_datetime의 타입을 date로 변경한 상태인데
      지금 알려주신 코드는
      mysql에서 데이터타입이 캐릭터일때 사용하는 코드인거 맞죠?

      저처럼 mysql에서 데이터타입을 date로 바꿔준 상태라면
      어떤 코딩만 해주면 되는건가요?

    • 샤쿠 syaku at 2016.07.13 10:05 신고

      Date reg_date = new Date(); 하면됩니다.
      아니면 mybatis xml맵퍼에서 디비 함수를 사용해두되구요~

      reg_date = now()

  • 아...며칠째인지..ㅠㅠ at 2015.08.26 23:14

    구글링으로 별 검색을 다하고 했는대도..에러를 못잡겠습니다..하..어떻게 해야될까요 ㅠㅜ

    심각: Servlet.service() for servlet [appServlet] in context with path [] threw exception [Request processing failed; nested exception is org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException:
    ### Error querying database. Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (Access denied for user 'spring'@'localhost' (using password: YES))
    ### The error may exist in file [E:\FoodTruck_Server\workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\TestServer\WEB-INF\classes\com\company\bbs\dao\BbsMapper.xml]
    ### The error may involve com.company.bbs.dao.BbsMapper.select
    ### The error occurred while executing a query
    ### Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (Access denied for user 'spring'@'localhost' (using password: YES))] with root cause
    java.sql.SQLException: Access denied for user 'spring'@'localhost' (using password: YES)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:946)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:2985)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:885)
    at com.mysql.jdbc.MysqlIO.secureAuth411(MysqlIO.java:3421)
    at com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:1247)
    at com.mysql.jdbc.Connection.createNewIO(Connection.java:2775)
    at com.mysql.jdbc.Connection.<init>(Connection.java:1555)
    at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:285)
    at org.apache.commons.dbcp.DriverConnectionFactory.createConnection(DriverConnectionFactory.java:38)
    at org.apache.commons.dbcp.PoolableConnectionFactory.makeObject(PoolableConnectionFactory.java:582)
    at org.apache.commons.dbcp.BasicDataSource.validateConnectionFactory(BasicDataSource.java:1556)
    at org.apache.commons.dbcp.BasicDataSource.createPoolableConnectionFactory(BasicDataSource.java:1545)
    at org.apache.commons.dbcp.BasicDataSource.createDataSource(BasicDataSource.java:1388)
    at org.apache.commons.dbcp.BasicDataSource.getConnection(BasicDataSource.java:1044)
    at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:111)
    at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:77)
    at org.mybatis.spring.transaction.SpringManagedTransaction.openConnection(SpringManagedTransaction.java:81)
    at org.mybatis.spring.transaction.SpringManagedTransaction.getConnection(SpringManagedTransaction.java:67)
    at org.apache.ibatis.executor.BaseExecutor.getConnection(BaseExecutor.java:279)
    at org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor.java:72)
    at org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:59)
    at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:267)
    at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:137)
    at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:96)
    at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:77)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:108)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:102)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:358)
    at com.sun.proxy.$Proxy9.selectList(Unknown Source)
    at org.mybatis.spring.SqlSessionTemplate.selectList(SqlSessionTemplate.java:198)
    at org.apache.ibatis.binding.MapperMethod.executeForMany(MapperMethod.java:119)
    at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:63)
    at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:52)
    at com.sun.proxy.$Proxy15.select(Unknown Source)
    at com.company.bbs.dao.BbsDao.getSelect(BbsDao.java:14)
    at com.company.bbs.ViewController.dispBbsList(ViewController.java:34)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:213)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:126)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:96)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:617)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:578)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:923)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:852)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:778)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1041)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:603)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)

  • 스프링초보자 at 2015.11.17 00:56

    에러가 밑에 처럼 나는데 혹시 왜 그런지 아세요 ? db는 오라클 쓰고있습니다.

    driverClassName=oracle.jdbc.driver.OracleDriver
    url=jdbc:oracle:thin:@localhost:1521:ORCL
    username=
    password=
    로 수정해줬구요 물론username랑 password 써줬어요
    그리고 context-datasource.xml은 밑에처럼해줬어요
    <bean id="jdbcProp" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location" value="classpath:jdbc.properties" />
    </bean>

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

    --------------------------------------------------------
    ERROR: org.springframework.web.context.ContextLoader - Context initialization failed
    org.springframework.beans.factory.BeanDefinitionStoreException: Invalid bean definition with name 'dataSourceSpied' defined in class path resource [config/spring/context/context-datasource.xml]: Could not resolve placeholder 'jdbc.driver'
    at org.springframework.beans.factory.config.PlaceholderConfigurerSupport.doProcessProperties(PlaceholderConfigurerSupport.java:209)
    at org.springframework.beans.factory.config.PropertyPlaceholderConfigurer.processProperties(PropertyPlaceholderConfigurer.java:220)
    at org.springframework.beans.factory.config.PropertyResourceConfigurer.postProcessBeanFactory(PropertyResourceConfigurer.java:84)
    at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:681)
    at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:656)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:446)
    at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:385)
    at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:284)
    at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:111)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4887)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5381)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
    at java.util.concurrent.FutureTask$Sync.innerRun(Unknown Source)
    at java.util.concurrent.FutureTask.run(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)

    • 샤쿠 syaku at 2015.11.17 10:06 신고

      설정정보(프로퍼티)에는
      driverClassName=oracle.jdbc.driver.OracleDriver
      url=jdbc:oracle:thin:@localhost:1521:ORCL
      username=
      password=

      이렇게 하셨다면

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

      jdbc. 을 삭제해주셔야합니다.

      로그에 보면 Could not resolve placeholder 'jdbc.driver' 오류가 있죠 찾지 못하는 거겠죠~

      참고: http://www.mkyong.com/spring/spring-propertyplaceholderconfigurer-example/

  • 뽕따이 at 2015.11.25 17:16

    샤쿠님 포스팅 잘보면 따라하고있는데... 막혀서 토나오겠네여 ㅠ

    HTTP Status 404 - /bbs/

    type Status report

    message /bbs/

    description The requested resource is not available.

    Apache Tomcat/7.0.62

    이런오류가나는데... 왜그런지 알수있을까요? Markers 에서보면

    Description Resource Path Location Type
    Referenced bean 'dataSource' not found context-mybatis.xml /syaku-bbs/src/main/resources/config/spring/context line 10 Spring Beans Problem

    요래된다고 나오는데.. ㅠ

    • 샤쿠 syaku at 2015.11.26 09:14 신고

      오류가 dataSource 를 찾지 못하는 것 같아요~
      dataSource 가 설정되어 있는 스프링 설정 파일을 확인해보세요~
      context-datasource.xml

  • 만년초보 at 2015.12.28 15:48

    안녕하세요 쥔장님 강의 잘보고 있습니다..
    근데 죄송한데..
    오라클 버전 BBS 테이블 생성하는거랑
    위에 BbsMapper.xml 오라클SQL 버전으로도 바꿔서 올려주심 안될까요..

    IDX가 컨트롤러에 막 정의되있고 왔다갔다해서 이해가 잘안갑니다 부탁드립니다.

  • replay at 2016.01.23 04:34

    감사합니다 감사합니다 처음으로 게시판 성공해보네요 ㅠㅠ
    왜 다른 프로젝트에 이상있는게 본 예제 실행에 영향을 끼친건지 엄한데서 고생 좀 했네요 -_-;;
    아무쪼록 좋은 공부 합니다 남은 파트도 잘 부탁드립니다.

  • lop at 2016.02.28 23:49

    유용한 소스네요~ 덕분에 공부를 잘 하고 있습니다! 감사합니다

  • 니콜 at 2017.07.11 17:56

    안녕하세요
    지금 해당 소스를 보면서 진행하고 있습니다..
    혹시 현 구조에서 DB를 2개를 사용한다는 가정하에 아래와 같이 작업했는데...
    같은 DB정보만 가져오는는 현상이 자꾸 발생하는데 혹시 각각 가지고 오려면 어떤 작업을 해야하는지 도움 부탁드립니다.

    1. context-database.xml 내 신규 DB datasource 추가
    2. context-mybatis.xml 내
    sqlSessionFactory2,
    sqlSessionMapperScan2 추가
    3. Bbs2Mapper.xml 파일 추가
    4. BbsDao.java, Bbs2Dao.java 파일에
    어노테이션 추가

    @Repository("bbsDao")
    @Resource(name="sqlSessionFactory")
    public interface BbsDao {

    dao부분만
    @Repository("bbs2Dao")
    @Resource(name="sqlSessionFactory2")
    public interface Bbs2Dao {

    @Resource(name="sqlSessionMapperScan2")도 적용해봤지만 결과동일

    • 샤쿠 syaku at 2017.07.20 16:17 신고

      DataSource 새로하나 만들고 id를 dataSource2 라고합니다.

      그리고 sqlSessionFactory 를 새로 하나만들어 이것도 2라고 합니다. 그리고 맵퍼를 스캔할때 와이드카드를 좀 다르게해서 올바르게 로드될수 있게 합니다.

      <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
      <property name="dataSource" ref="dataSource2" />
      <property name="configLocation" value="classpath:config/mybatis-config-base.xml" />
      <property name="mapperLocations">
      <list>
      <value>classpath*:mapper/*Mapper2.oracle.xml</value>
      </list>
      </proper ...

      맵퍼를 스캔할때 기존에 있는 다른 세션팩토리가 해당 팹퍼를 읽지 못하게 와일드카드를 잘 분리해주세요

      그리고 sqlsession 2로 추가하고

      마지막으로 MapperScan 용 어노테이션을 하나만듭니다.

      <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
      <property name="basePackage" value="..." />
      <property name="annotationClass" value="...MapperScan2" />
      <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory2" />
      </bean>


  • 니콜 at 2017.07.20 18:02

    답글 너무 감사드립니다.

    위 방식대로 진행했습니다. 서버올라가는건 문제 없는데 프로그램실행하면
    첫번째 DB조회 성공 후
    두번째 DB접근할때 에러가 발생합니다. 조금만 디버깅 도움 부탁드립니다.

    작업내용

    순서1) BbsDao2.java 추가
    (
    @Repository(value = "bbsDao2")
    public interface BbsDao2 {
    List<BbsVo> select2();
    }
    )
    순서2) datasource2 추가
    순서3) sqlSessionFactory2 추가
    순서4) MapperScannerConfigure 추가

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="configLocation" value="classpath:config/spring/context/mybatis-config-base.xml" />
    <property name="mapperLocations">
    <list>
    <value>classpath:sql/*Mapper.xml</value>
    </list>
    </property>
    </bean>

    <!-- scan for mappers and let them be autowired -->
    <bean id="sqlsession" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.hist.bbs" />
    <property name="annotationClass" value="org.springframework.stereotype.Repository"/>
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
    </bean>


    <bean id="sqlSessionFactory2" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource2" />
    <property name="configLocation" value="classpath:config/spring/context/mybatis-config-base.xml" />
    <property name="mapperLocations">
    <list>
    <value>classpath:sql/BbsMapper.oracle.xml</value>
    </list>
    </property>
    </bean>

    <!-- scan for mappers and let them be autowired -->
    <bean id="sqlsession2" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.hist.bbs" />
    <property name="annotationClass" value="com.hist.bbs.dao.BbsDao2"/>
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory2" />
    </bean>


    에러로그
    HTTP Status 500 - Request processing failed; nested exception is org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.hist.bbs.dao.BbsDao2.select2



  • 샤쿠 syaku at 2017.07.20 18:08 신고

    맵퍼에 해당하는 dao interface 를 찾지 못한는 오류입니다.

    annotationClass 에는 @MapperScan 같은 어노테이션 클래스를 설정해야합니다. 그리고 dao 클래스에 위 어노테이션을 삽입해야합니다.

  • 니콜 at 2017.07.21 09:29

    샤쿠님이 정의해주신 부분도 확인해보고 있습니다.
    아래 twomapperscan 구문을
    아래 출처처럼 동일하게 사용해봤지만 작동은 안되더라구요..
    혹시 도움 더 부탁드리겠습니다.

    적용1) xml 내 twomapperscan 설정
    <!-- scan for mappers and let them be autowired -->
    <bean id="sqlsession2" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.hist.bbs" />
    <property name="annotationClass" value="com.hist.bbs.dao.TwoMapperScan"/>
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory2" />
    </bean>


    적용2) 아래 출처와 같이 mapper 클래스 생성
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented @Component public
    @interface TwoMapperScan {
    String value() default "";
    }
    출처: http://syaku.tistory.com/303 [Syaku 샤쿠]

    작업3) BbsDao2.java에 적용
    @Repository(value = "bbsDao2")
    @TwoMapperScan
    public interface BbsDao2 {
    List<BbsVo> select2();
    }

    • 샤쿠 syaku at 2017.07.21 09:54 신고

      이전 댓글에 보니

      <property name="annotationClass" value="org.springframework.stereotype.Repository"/>

      리파지토리 어노테이션으로 스캔을 하고 있습니다.

      그렇다면 BbsDao2 에서 저 어노테이션을 사용해서는 안됩니다.

      // @Repository(value = "bbsDao2")
      // 그리고 value 는 자동으로 첫글자가 소문자로 만들어지기때문에 자동으로 bbsDao2 가 만집니다. 그래서 value는 사용하지 않아도 됩니다.
      @TwoMapperScan
      public interface BbsDao2 {
      List<BbsVo> select2();

      -----------------
      sqlSession 에서 스캔된 dao(인턴페이스 멥퍼)는
      sqlSession2 에서 다시 스캔될 수 없습니다.
      둘을 잘 분리해줘야 합니다.

      맵퍼 스캔은 해당 패키지 경로 하위에 @annotation 적용된 클래스만 로드됩니다.

      그리고 인터페이스 맵퍼와 xml 맵퍼가 서로 바인딩(자바 리플렉션에 의해)됩니다.



  • 니콜 at 2017.07.21 10:58

    샤쿠님 감사합니다.^^ 해결했습니다.

    multi datasource를 구현하기 위해 2가지 방식이 있는데
    샤쿠님이 보여주신거 mapperScan 방식 그리고 sqlsessionTemplete 방식이 있다는거 알게되었습니다.


    혹시 다른 분들도 저처럼 하실 분들은 관련 자료는 아래 참고하시고
    소스에 적용해보세요
    적용전 twoMapper 어노테이션 참고 자료 꼭 보시고요..
    참고1) mapper 인터페이스에 선언된 어노테이션 설명
    http://blog.naver.com/cracker542/40159657935
    참고2) mapper 인터페이스를 dao에 어떻게 적용하는지 샘플
    http://devyongsik.tistory.com/357
    http://mansookim.tistory.com/96

    이제부터 적용한 방법입니다. 위 댓글부터 보시면 이해되실겁니다.
    최종작업1) TwomapperScan.java 생성

    @Target({ ElementType.TYPE })
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Component
    public @interface TwoMapperScan {
    //String query();
    }


    작업2) datasource2.xml 접근할 BbsDao2.java 에 @TwoMapperScan 추가
    @TwoMapperScan
    public interface BbsDao2 {
    List<BbsVo> select2();
    }

    소스파일
    http://blog.naver.com/eungsik80/221056405727

  • 샤쿠 syaku at 2017.07.21 12:11 신고

    수고하셧네요 :)

댓글 남기기
◀ PREV 1···99100101102103104105106107···313 NEXT ▶