레이블이 SPRING FRAMEWORK인 게시물을 표시합니다. 모든 게시물 표시
레이블이 SPRING FRAMEWORK인 게시물을 표시합니다. 모든 게시물 표시

2015년 8월 5일 수요일

Spring+Mybatis 사용간 이슈 정리

1. model 객체에 String 을 담아 sql 문 실행시 에러 발생

  1) 관련 소스
....
if(~~~)
 String tempDate = sdFormat.format(nowDate);

 model.addAttribute("list", dao.listDao(tempDate));
 model.addAttribute("view_date", tempDate);
}
.....


  2) 에러 구문
org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'param1' in 'class java.lang.String'

  3) 원인
    해당 변수에 getter를 보고 없으면 내는 에러
     String만 발생하고, if구문 이하 절에서만 발생한다.
     (int, long 등은 발생하지 않고, 조건절 없이 진행시 String도 문제 없었음)

    관련하여 잘 정리한 블로그 글
http://everydaycoding.tistory.com/category/Springframework

  4) 해결 방안
    위 블로그에서 확인 가능
    정리하자면 DTO 만들어서 보내거나 지정 변수를 쓰면 가능
     (무조건 getter를 찾는데 있으면 실행에 이상없음)
    여기서는 DTO를 만드는게 좀더 빨리 해결되어 아래처럼 수정하여
     이슈 완료.

Controller.java (History_Date_dto 라는 객체를 만들어 통째로 넣음)
 History_Date_dto tempDate = new History_Date_dto();
 tempDate.setReservDate(sdFormat.format(nowDate));

 model.addAttribute("list", dao.listDao(tempDate));

History_Date_dto.java (객체 생성 getter, setter 보유)
package com.javalec.spring_pjt_board.dto;

public class History_Date_dto {
String reservDate;

public History_Date_dto(){

}

public History_Date_dto(String reservDate){
this.reservDate = reservDate;
}

public String getReservDate() {
return reservDate;
}

public void setReservDate(String reservDate) {
this.reservDate = reservDate;
}

}

BDao.java (DAO 단에서도 통째로 넣으면 이상없이 진행됨)
ArrayList<History_Dto> listDao(History_Date_dto tempDate);


2015년 8월 3일 월요일

신입SW인력을 위한 실전 자바(Java) 스프링(Spring) 동영상과정 28~30 Mybatis

이 게시물은
http://www.wiz.center/258
http://www.wiz.center/259
http://www.wiz.center/260

해당 링크를 청취하고 작성한 글입니다.

본인 이해도 확인을 위해서 작성한 것이니
스프링에 대해서 알고 있다면 쑥 훝어보고 끝내시고

좀 깊게 알고 싶다면 위의 링크부터 시작해서
총 30개 강의로 이루어진 스프링 과정을 청취하세요


1. Mybatis 사용 준비(기본 설정)
    Java에서 작성한 쿼리부분을 XML로 대체하여 간단하게 사용
    하고자 하여 만들어진 라이브러리

예제1) 강의안에 주어진 예제를 mysql 용으로 만들기

작성1 예제에는 이미 설정되어 있지만 Mybatis를 사용하려면
         해당 jar 파일을 추가해야 된다.
         pom.xml에 다음 내용이 있는지확인 및 추가

  
   org.mybatis
   mybatis
   3.2.8
  
  
   org.mybatis
   mybatis-spring
   1.2.2
  


작성 2. 예제 작성을 위해 28-1 예제를 import 및
          board 라는 테이블을 생성

CREATE TABLE IF NOT EXISTS `board` (
  `mId` int(11) NOT NULL AUTO_INCREMENT,
  `mWriter` varchar(100) NOT NULL,
  `mContent` varchar(300) NOT NULL,
  PRIMARY KEY (`mId`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;


작성 3. pom.xml 에 mysql_connection 플러그인 추가



 mysql
 mysql-connector-java
 5.1.31



작성 4. web.xml 의 db connection 을 아래와 같이 변경


  
  
  
  
 

 
  
 


작성 5. insert 쿼리 변경

 @Override
 public void writeDao(final String mWriter, final String mContent) {
  System.out.println("writeDao()");
  this.template.update(new PreparedStatementCreator() {
   @Override
   public PreparedStatement createPreparedStatement(Connection con)
     throws SQLException {
//    String query = "insert into board (mId, mWriter, mContent) values (board_seq.nextval, ?, ?)";
    String query = "insert into board (mWriter, mContent) values (?, ?)";
    PreparedStatement pstmt = con.prepareStatement(query);
    pstmt.setString(1, mWriter);
    pstmt.setString(2, mContent);
    return pstmt;
   }
  });
 }


결과 ) web server 띄운후 아래 링크로 리스트 뜨는거 확인
        별루 변경안해도 되니 굿.!!!!
http://localhost:8181/spring_mybatis/list

2. Mybatis 사용하기위한 작업 (list 보는거 적용하기)
    사용해보면 좀더 직관적이고 사용하기 편하기 때문에
    Mybatis를 사용하게 된다고 한다.

-------------------------------

예제 2 기존 Spring 구현 예제를 Mybatis 적용하여 구현하기

작성1 servlet-context.xml 에 다른 bean 객체를 추가한다.
       이때 sqlSessionFactory 의 property mapperLocations 에게
       주어지는 value 값이 쿼리를 작성할 xml 위치이다.
      그리고 Java 단에서 사용할 객체는 팩토리를 포함한
      sqlSession 임


 
 

 

 



작성2 com.javalec.spring_mybatis.dao.mapper 패키지를 생성하고
        그 밑에 IDao.xml 를 생성하여 아래와 같이 작성한다.
        여기서는 list 조회 sql 구문은 적용한 상태이다.
     mapper namespace 는 interface 위치를 기입한다.
     각 실행문 id 는 interface에 명시되어 있는 메서드명을 입력한다.
     resultType 은 각 실행 결과를 담을 타입을 명시해준다. (풀 경로 기입)
        안에 쿼리를 기입한다.

<?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.javalec.spring_mybatis.dao.IDao">
<select id="listDao" resultType="com.javalec.spring_mybatis.dto.ContentDto">
SELECT * FROM BOARD ORDER BY MID DESC
</select>

<insert id="writeDao">

</insert>

<delete id="deleteDao">

</delete>
</mapper>


작성3 Controller.java 에 멤버변수 를 하나 추가
          bean 등록이 되어 있기 때문에 setter 없이
          @Autowired 로 추가 가능함.

        @Autowired
 private SqlSession sqlSession;


작성3-2) Controller.java 에 list 요청에 대한 응답을 작성
            이전의 dao 객체의 메서드를 호출하는 대신
             sqlSession 의 setMapper(); 를 호출하여
             xml 의 해당 sql이 진행되도록 연결해주면
            값을 바로 모델에 넣어주고 리턴이 된다.

@RequestMapping("/list")
public String list(Model model) {
// ArrayList dtos = dao.listDao();
 IDao dao = sqlSession.getMapper(IDao.class);
// ArrayList dtos = dao.listDao();
 model.addAttribute("list", dao.listDao());
 return "/list";
}


결과 )  리스트가 정상적으로 뜨면 mybatis가 잘 적용됨.


----------------------------------------------------------

3. mybatis 사용시 조건 추가하기 (delete, insert 적용)
-------------------------------
예제 계속 진행

작성1 IDao.xml 수정
         여기서 insert 에서 ? 대신에 #{param1} 을 작성
          param1~ 예약어로 param뒤에 숫자는 순서대로
          작성하는게 원칙임 여기서 앞에 # 과 $를 붙일 수 있는데
          #{param0} : 컬럼 형식에 맞춰서 입력값을 형변환함
          ${param0} : 입력 데이터 형식을 그대로 입력


 INSERT INTO BOARD (MWRITER, MCONTENT) VALUES (#{param1}, #{param2})


 DELETE FROM BOARD WHERE MID = #{param1}



작성2 HomeController.java 의 2개 메서드를 아래와 같이 수정
          여기서 파라미터는 입력한 순서대로 param1~~~ 으로
          대응된다.

@RequestMapping("/write")
public String write(HttpServletRequest request, Model model) {
 IDao dao = sqlSession.getMapper(IDao.class);
 dao.writeDao(request.getParameter("mWriter"), request.getParameter("mContent"));
 return "redirect:list";
}

@RequestMapping("/delete")
public String delete(HttpServletRequest request, Model model) {
 IDao dao = sqlSession.getMapper(IDao.class);
 dao.deleteDao(request.getParameter("mId"));
 return "redirect:list";
}


결과 ) http://localhost:8181/spring_mybatis/list
    접속후 삭제 및 글 등록이 제대로 되는지 확인
----------------------------------------------------------

mybatis 문서 가이드
(좀더 복잡한 경우에 대한 적용방법에 대해서 확인 가능)

http://blog.mybatis.org/

신입SW인력을 위한 실전 자바(Java) 스프링(Spring) 동영상과정 25~27 Security

이 게시물은
http://www.wiz.center/255
http://www.wiz.center/256
http://www.wiz.center/257

해당 링크를 청취하고 작성한 글입니다.

본인 이해도 확인을 위해서 작성한 것이니
스프링에 대해서 알고 있다면 쑥 훝어보고 끝내시고

좀 깊게 알고 싶다면 위의 링크부터 시작해서
총 30개 강의로 이루어진 스프링 과정을 청취하세요

1. 보안 관련 환경 설정 방법
-------------------------------
예제 1
작성1 pom.xml 에 다음 의존을 추가(보안관련) 꽤 많은데...
         다음 내용을 추가


 org.springframework.security
 spring-security-config
 3.2.5.RELEASE



 org.springframework.security
 spring-security-core
 3.2.5.RELEASE



 org.springframework.security
 spring-security-web
 3.2.5.RELEASE



 org.springframework.security
 spring-security-taglibs
 3.2.4.RELEASE



작성2. security-context.xml 라는 설정 파일을 servlet-context 폴더에
         작성하고 아래와 같이 작성한다.
         여기에서는 user를 직접 명시하고 있음(db를 접속하지 않고 진행)
 작성할때 new - Spring bean Configuration 파일 선택이 가능
        -> 파일명 작성 -> 사용할 bean 종류를 선택 (security 선택 가능)
        -> security 선택시 버전별로 선택가능
        -> 완료



 
  
  
 



작성3 web.xml에 설정 추가


  contextConfigLocation
  
   /WEB-INF/spring/root-context.xml
   /WEB-INF/spring/appServlet/security-context.xml
   
 
......


결과 )  security를 사용하기 위한 설정 끝


----------------------------------------------------------

2. 인 메모리형태로 로그인 보안 설정 방법
-------------------------------
예제 계속 진행

작성1 web.xml 에 fillter 추가



      
        springSecurityFilterChain
        org.springframework.web.filter.DelegatingFilterProxy
    
    
        springSecurityFilterChain
        /*
    


작성2 security-context.xml 에 아래 내용을 추가



 
  
  
 
 
 
  
   
    
    
   
  
 



작성3 Controller.java 작성

package com.javalec.spring_security_pjt;

import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/**
 * Handles requests for the application home page.
 */
@Controller
public class HomeController {
 
 private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
 
 /**
  * Simply selects the home view to render by returning its name.
  */
 @RequestMapping(value = "/index.html", method = RequestMethod.GET)
 public String home(Locale locale, Model model) {
  logger.info("Welcome home! The client locale is {}.", locale);
  
  Date date = new Date();
  DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
  
  String formattedDate = dateFormat.format(date);
  
  model.addAttribute("serverTime", formattedDate );
  
  return "home";
 }
 
 @RequestMapping("/login.html")
 public String login(Locale locale, Model model) {
  
  return "security/login";
 }
 
 @RequestMapping("/welcome.html")
 public String welcome(Locale locale, Model model) {
  
  return "security/welcome";
 }
}


작성4 views 폴더 밑에 index.jsp 작성
        (로그인 후 이름 보여줌)

<%@ page contentType="text/html;charset=utf-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
    <title>Home</title>
</head>
<body>
<h1>
    Hello world!
</h1>

<P>Username is ${username}</P>
<br>
<a href="<c:url value="j_spring_security_logout" />" target="_self">Logout</a>
</body>
</html>

작성5 views 밑에 home.jsp 작성 (별 내용은 없음)
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
<title>Home</title>
</head>
<body>
<h1>
Hello world!
</h1>

<P>  The time on the server is ${serverTime}. </P>
</body>
</html>


작성6 views/security 밑에 login.jsp 작성

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
login.jsp
</body>
</html>


작성7 views/security 밑에 welcome.jsp 작성

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
welcome.jsp
</body>
</html>


결과 ) localhost:8181/spring_security_pjt/login.html 접속
http://localhost:8181/spring_security_pjt/spring_security_login;jsessionid=B2716C95E6BB1302CF18F01226DB0236
이렇게 이동하면서 로그인 창이 뜸
알맞게 입력시
user -> login
admin -> admin
----------------------------------------------------------
여기에서는 어디로 접속하든지 spring 에서 제공하는
   로그인 페이지로 안내되어 처리됨
 (로그인 화면을 따로 작성하지 않음)


3. 로그인, 아웃 페이지 작성, 상태를 표시하기

-------------------------------
예제 1  기존 예제 그대로 사용

작성1 security-context.xml 에 다음 내용을 추가
        페이지를 설정해주면 같은 조건의 로그인이 필요한 사안에
        대해서 spring 에서 제공하는 페이지 정보가 아닌
        명시한 loginForm.html 이라는 페이지로 보낸다.

 
  
  
  
 


작성2 security/loginForm.jsp 작성
        인증 절차를 거치기 위한 name, value가 진한 부분같이
        작성해줘야 함
       taglib 러리 이용해서 작성함 (security uri를 call 하기 위해서)

<%@ 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 PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>loginForm.jsp</h1>
<form action="<c:url value="j_spring_security_check" />" method="post">
ID : <input type="text" name="j_username"> <br />
PW : <input type="text" name="j_password"> <br />
<input type="submit" value="LOGIN"> <br />
</form>
</body>
</html>


작성3 Controller.java 에 해당 호출을 추가

 @RequestMapping("loginForm.html")
 public String loginForm(Locale locale, Model model){
  return "security/loginForm";
 }


결과 )  http://localhost:8181/spring_security_pjt/
      welcom 화면 뜸
         http://localhost:8181/spring_security_pjt/login.html

        loginForm.jsp 를 호출함


+ 로그인 실패시 처리 방법
작성4 security-context.xml 수정

 
      
          
       



작성5 loginForm.jsp <body> 부분 수정

<h1>loginForm.jsp</h1>

<c:url value="j_spring_security_check" var="loginUrl"/>
<form action="${loginUrl}" method="post">
<c:if test="${param.ng != null}">
<p>
LogIn NG! <br />
<c:if test="${SPRING_SECURITY_LAST_EXCEPTION != NULL}">
message : <c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}" />
</c:if>
</p>
</c:if>
ID : <input type="text" name="j_username"> <br />
PW : <input type="text" name="j_password"> <br />
<input type="submit" value="LOGIN"> <br />
</form>

실행 ) 로그인 실패시 폼 내용이 ng 값 여부에 따라 변경되어 보여짐
        로그인 성공 -> 기존과 동일
    주황색 글시에서 logout을 처리하는 spring 코드가 있음
       id와 같이   j_spring_security_logout 을 보내면
       로그아웃 처리됨.

+ 로그아웃 처리 방법
작성6 login.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 PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
login.jsp
<c:if test ="${not empty pageContext.request.userPrincipal } ">
<p>is Log-in</p>
</c:if>

<c:if test="${empty pageContext.request.userPrincipal }">
<p> is Log-Out</p>
</c:if>

USER ID : ${pageContext.request.userPrincipal.name}<br/>
<a href="${pageContext.request.contextPath}/j_spring_security_logout">Log Out</a> <br />
</body>
</html>


4. 보안관련 taglis 사용방법


 org.springframework.security
 spring-security-taglibs
 3.2.4.RELEASE



처음에 미리 추가해놨음
태그 라이브러리를 추가함으로서
  jsp View 단 구현시 좀더 간단하게 구현이 가능함.

작성1 기존  login.jsp 파일에서
<%@ taglib uri="http://www.springframework.org/security/tags" prefix="s" %>
<!-- 태그 라이브러리를 추가한다 -->
....
<c:if test ="${not empty pageContext.request.userPrincipal } ">
<p>is Log-in</p>
</c:if>

<c:if test="${empty pageContext.request.userPrincipal }">
<p> is Log-Out</p>
</c:if>
<!-- s 태그를 이용해서 작성한다 -->
.....

USER ID : ${pageContext.request.userPrincipal.name}<br/>
.......


부분을 아래 문법으로 변경할 수 있음.

<s:authorize ifAnyGranted="ROLE_USER">
<p> is Log-In</p>
</s:authorize>

<s:authorize ifNotGranted="ROLE_USER">
<p> is Log-Out</p>
</s:authorize>
......

USER ID : <s:authentication property="name"/><br/>
.......


2015년 7월 30일 목요일

신입SW인력을 위한 실전 자바(Java) 스프링(Spring) 동영상과정 22~24_Transaction[트랜젝션]

이 게시물은
http://www.wiz.center/252
http://www.wiz.center/253
http://www.wiz.center/254

해당 링크를 청취하고 작성한 글입니다.

본인 이해도 확인을 위해서 작성한 것이니
스프링에 대해서 알고 있다면 쑥 훝어보고 끝내시고

좀 깊게 알고 싶다면 위의 링크부터 시작해서
총 30개 강의로 이루어진 스프링 과정을 청취하세요

1. 트랜젝션 개념
    하나의 작업이 완료되는 단위
     하나의 작업이 db로 입력 2번 이상 진행되는 경우
     끝까지 정상 진행되는 경우에는 모두 입력
     순서 진행중 하나라도 오류, 조건이 맞지 않는 경우에는
     처음부터 끝까지 작업을 되돌린다.
    이런 연결된 업무를 하나의 트랜젝션 이라고 한다.


-------------------------------
예제 1. 트랜잭션 기능 확인
http://tip.daum.net/question/60526161
--mysql 에 제약조건 넣은 방법은 위 글 참고
--   결과적으로 mysql 에 제약조건을 넣어도
--   적용되지 않아서 oracle 처럼 db에서 걸기보다는
--   java단이나 그 이하단에서 걸어줘야 겠습니다.


작성1 table 생성
card, ticket 두개 테이블을 생성한다.

CREATE TABLE IF NOT EXISTS `card` (
  `consumerid` varchar(100) NOT NULL,
  `amount` int(4) not null
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


CREATE TABLE IF NOT EXISTS `ticket` (
  `consumerid` varchar(100) NOT NULL,
  `countnum` int(4) not null
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


작성2. 상단 252번 강의에서  22-1예제를 import
          및 servlet.xml 의 oracle 컨넥션 풀을 아래와 같이
          mysql용으로 변경

 
  
  
  
  
 


작성3. dao 단에서 강제로 입력이 안되도록 설정한다.

  template.update(new PreparedStatementCreator() {
   
   @Override
   public PreparedStatement createPreparedStatement(Connection con)
     throws SQLException {
    PreparedStatement pstmt = null;
    if(Integer.parseInt(dto.getAmount()) < 5){
     String query = "insert into ticket (consumerId, countnum) values (?, ?)";
     pstmt = con.prepareStatement(query);
     pstmt.setString(1, dto.getConsumerId());
     pstmt.setString(2, dto.getAmount());
    }
    return pstmt;
   }
  });


결과 )  
 - kim
3 입력시 정상 입력됨
 - park
5 입력시 500에러가 발생하였음
   + card 는 정상 입력되었으나 ticket 은 입력되지 않음
여기서 card도 입력되지 않았어야 함
   (트랜젝션이 적용시 이부분을 같이 진행 가능함)

2. 트랜젝션 사용방법
-------------------------------
예제 2 그대로 사용

작성1  servlet.xml 에 다음 bean 을 추가


 


 
  
  
 


<!-- dao는 기존에 property 중 datasource를 삭제하고
       transactionManager를 받도록 설정한다 -->


작성2  DAO에 PlatformTransactionManager  객체를 추가
         byeTicket 함수를 아래와 같이 수정

public void buyTicket(final TicketDto dto) {
  System.out.println("buyTicket()");
  System.out.println("dto.getConsumerId() : " + dto.getConsumerId());
  System.out.println("dto.getAmount() : " + dto.getAmount());
  
  TransactionDefinition definition = new DefaultTransactionDefinition();
  TransactionStatus status = transactionManager.getTransaction(definition);
  //기본 클래스인 definition 클래스를 설정
                // 이후 좀더 간단한 구문으로 수정 예정
  try{
  template.update(new PreparedStatementCreator() {
   
   @Override
   public PreparedStatement createPreparedStatement(Connection con)
     throws SQLException {
    String query = "insert into card (consumerId, amount) values (?, ?)";
    PreparedStatement pstmt = con.prepareStatement(query);
    pstmt.setString(1, dto.getConsumerId());
    pstmt.setString(2, dto.getAmount());
    
    return pstmt;
   }
  });
  
  template.update(new PreparedStatementCreator() {
   
   @Override
   public PreparedStatement createPreparedStatement(Connection con)
     throws SQLException {
    PreparedStatement pstmt = null;
    if(Integer.parseInt(dto.getAmount()) < 5){
     String query = "insert into ticket (consumerId, countnum) values (?, ?)";
     pstmt = con.prepareStatement(query);
     pstmt.setString(1, dto.getConsumerId());
     pstmt.setString(2, dto.getAmount());
    }
    return pstmt;
   }
  });
  transactionManager.commit(status);
  
  }catch(Exception e){
   transactionManager.rollback(status);
                        System.out.println("this data rollback!! ");
  }
 }


결과 ) 테스트 시 5이상의 숫자를 넣어도 에러가
         나지 않고 정상적으로 진행되는것 처럼 보이지만
      실제 db에는 입력되지 않고 (card, ticket 둘다)
       rollback 함수가 실행된걸 확인 할 수 있었다.
     // oracle과 틀린점 -> oracle에서는 db에서 에러를 냈기 때문에
     // 콘솔에 에러가 찍힘

3. TransactionTemplate  을 활용하여 Transaction 을
     좀더 간단하게 사용할 수 있다.

-------------------------------
예제 1 기존 수정하던 예제


작성1 DAO 수정
        객체는 TransactionTemplate  로 생성하고
        하단 try catch 부분을 삭제하고
     
         객체명.execute(new TransactionCallbackWithoutResult(){
          .....
          }
         .... 안에 기존에 작성한 template
          파일을 통째로 집어넣는다.
package com.javalec.spring_pjt_ex.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

import com.javalec.spring_pjt_ex.dto.TicketDto;

public class TicketDao {

 JdbcTemplate template;
 
// PlatformTransactionManager transactionManager;
// 
// 
// public void setTransactionManager(PlatformTransactionManager transactionManager) {
//  this.transactionManager = transactionManager;
// }
 
 TransactionTemplate transactionTemplate;

 public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
  this.transactionTemplate = transactionTemplate;
 }

 public void setTemplate(JdbcTemplate template) {
  this.template = template;
 }
 
 public TicketDao() {
  System.out.println(template);
 }
 
 public void buyTicket(final TicketDto dto) {
  System.out.println("buyTicket()");
  System.out.println("dto.getConsumerId() : " + dto.getConsumerId());
  System.out.println("dto.getAmount() : " + dto.getAmount());
  
  transactionTemplate.execute(new TransactionCallbackWithoutResult() {
   
   @Override
   protected void doInTransactionWithoutResult(TransactionStatus arg0) {
    template.update(new PreparedStatementCreator() {
     
     @Override
     public PreparedStatement createPreparedStatement(Connection con)
       throws SQLException {
      String query = "insert into card (consumerId, amount) values (?, ?)";
      PreparedStatement pstmt = con.prepareStatement(query);
      pstmt.setString(1, dto.getConsumerId());
      pstmt.setString(2, dto.getAmount());
      
      return pstmt;
     }
    });
    
    template.update(new PreparedStatementCreator() {
     
     @Override
     public PreparedStatement createPreparedStatement(Connection con)
       throws SQLException {
      PreparedStatement pstmt = null;
      if(Integer.parseInt(dto.getAmount()) < 5){
       String query = "insert into ticket (consumerId, countnum) values (?, ?)";
       pstmt = con.prepareStatement(query);
       pstmt.setString(1, dto.getConsumerId());
       pstmt.setString(2, dto.getAmount());
      }
      return pstmt;
     }
    });
   }
  });
  
//  TransactionDefinition definition = new DefaultTransactionDefinition();
//  TransactionStatus status = transactionManager.getTransaction(definition);
  
//  try{
//  
//  
//  
//  transactionManager.commit(status);
//  
//  }catch(Exception e){
//   transactionManager.rollback(status);
//   System.out.println("this data rollback!! ");
//  }
 }
}


작성2 servlet-context.xml 을 아래와 같이 수정한다.

 
  
 
 
 
  
 
 
 
  
  
 


결과 ) 이전 동작과 같이 정상 동작한다.

----------------------------------------------------------

4. TransactionTemplate  전파 속성 설정하기
         2개이상의 트랜젝션이 동작하고 있을때
          새로 들어가는 트랜젝션이 기존에 동작하고 있는
          트랜젝션과의 관계를 설정하는 기능입니다.
    먼저 속성에 대해 알아볼수 있는 예제 수정을 진행하고
      기능을 하나하나 알아보는 형태로 진행

 예제) 이전 예제에 트랜젝션을 추가하여
          2개 이상의 트랜젝션이 돌아가도록 수정한다.
작성1. servlet-context.xml 에 transactionTemplate를 2개 추가하여
         각각 dao, command(service) 에 할당


 

 
 
   
    

 
   
    


  
  
 
 

  
  



작성2. 패키지 추가com.javalec.spring_pjt_ex.command
         서비스를 담당할 패키지를 추가하고 그 밑에 인터페이스와
         구현체를 서술한다.
인터페이스 : ITicketCom

package com.javalec.spring_pjt_ex.command;

import com.javalec.spring_pjt_ex.dto.TicketDto;

public interface ITicketCom {
 public void execute (TicketDto ticketdto);
}

구현체 : TicketCom implements ITicketCom

package com.javalec.spring_pjt_ex.command;

import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

import com.javalec.spring_pjt_ex.dao.TicketDao;
import com.javalec.spring_pjt_ex.dto.TicketDto;

public class TicketCom implements ITicketCom {
 private TicketDao ticketDao;
 private TransactionTemplate transactionTemplate2;
 
 
 public void setTicketDao(TicketDao ticketDao) {
  this.ticketDao = ticketDao;
 }

 public void setTransactionTemplate2(TransactionTemplate transactionTemplate2) {
  this.transactionTemplate2 = transactionTemplate2;
 }

 @Override
 public void execute(final TicketDto ticketDto) {
  transactionTemplate2.execute(new TransactionCallbackWithoutResult() {
   
   
   @Override
   protected void doInTransactionWithoutResult(TransactionStatus arg0) {
    ticketDto.setAmount("1");
    ticketDao.buyTicket(ticketDto);
    ticketDto.setAmount("5");
    ticketDao.buyTicket(ticketDto); 
   }
  });
 }
}


작성3. controller 에서 변수 추가 및 호출을 dto로 하지 않고
         서비스를 거쳐서 진행되도록 변경한다.


.....
// private TicketDao dao;
 
 private ITicketCom ticketCom;
 
 @Autowired
 public void setTicketCom(ITicketCom ticketCom) {
  this.ticketCom = ticketCom;
 }
 
// @Autowired
// public void setDao(TicketDao dao) {
//  this.dao = dao;
// }
.......
 @RequestMapping("/buy_ticket_card")
 public String buy_ticket_card(TicketDto ticketDto, Model model) {
..................
  //dao.buyTicket(ticketDto);
  ticketCom.execute(ticketDto);
..................


실행시 값을 몇을 넣든 1,5 두번 실행될텐데 여기서는
    끝에 에러가 났겠지만 1을 넣어서 시작한 값부터
     끝까지 다 rollback 해버렸다.

이는 설정 파일의 
<beans:property name="propagationBehavior" value="0"></beans:property> 
옵션과 관계가 있다.  0은 전체 의존처리 이기 때문에
   해당 옵션과 맞춰 에러가 날 경우 전체 rollback 처리함.


이후 service(...com) 에서 작성한 트랜젝션과
       dto 안에 설정된 트랜젝션이 따로 동작하는걸로
       설정되어 있는데
       (트랜젝션1 은 DAO 에서 호출하고
        트랜젝션2 는 service(...com) 에서 사용하고 있음)
       이 둘 관계를 관찰하면서 각 속성을 알아보겠음

db 입력순서는

①card 이름 1
②ticket 이름 1
③card 이름 5
④ticket 이름 5


 1) PROPAGATION_REQUIRED(0)
    : 전체 의존 처리
작성1: servlet-context.xml 수정

 
  
  
 
 
 
  
  
 


     예제에서는 5에서 에러 났지만 1로 셋팅된 이전까지 취소됨
     DB에 한건도 입력되지 않았음


 2) PROPAGATION_SUPPORTS(1)
    : 기존 트랜젝션에 의존

 
  
  
 
 
 
  
  
 


     예제에서는 5에서 에러 났지만 1로 셋팅된 이전까지 취소됨
     서비스 객체를 1로 셋팅했지만 전체가 걸려있기 때문에
     1이나 0이나 별반 차이가 없었음.
   
     DB에 한건도 입력되지 않았음
서비스에서 객체 호출시 트랜젝션을 적용하지 않고 진행

@Override
 public void execute(final TicketDto ticketDto) {
  ticketDto.setAmount("1");
  ticketDao.buyTicket(ticketDto);
  ticketDto.setAmount("5");
  ticketDao.buyTicket(ticketDto); 
  
  
//  transactionTemplate2.execute(new TransactionCallbackWithoutResult() {
//   
//   
//   @Override
//   protected void doInTransactionWithoutResult(TransactionStatus arg0) {
//    ticketDto.setAmount("1");
//    ticketDao.buyTicket(ticketDto);
//    ticketDto.setAmount("5");
//    ticketDao.buyTicket(ticketDto); 
//   }
//  });
 }


     DB에 ③ 까지 입력됨
      하나의 DB 쿼리 실행이 하나의 트랜젝션으로 처리됨


 3) PROPAGATION_MANDATORY(2)
    :  트랜젝션에 꼭 포함 (트랜젝션 있는데서 호출해야 됨)


  
  
 
 
 
  
  
 


     실행시 DB 까지 진행되기 전에 에러처리됨
 Request processing failed; nested exception is org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'

        트랜젝션이 걸리지 않은 서비스에서 호출하기 때문에
        이렇게 뜸

@Override
 public void execute(final TicketDto ticketDto) {
//  ticketDto.setAmount("1");
//  ticketDao.buyTicket(ticketDto);
//  ticketDto.setAmount("5");
//  ticketDao.buyTicket(ticketDto); 
  
  
  transactionTemplate2.execute(new TransactionCallbackWithoutResult() {
   
   
   @Override
   protected void doInTransactionWithoutResult(TransactionStatus arg0) {
    ticketDto.setAmount("1");
    ticketDao.buyTicket(ticketDto);
    ticketDto.setAmount("5");
    ticketDao.buyTicket(ticketDto); 
   }
  });
 }  

   
     처음부터 적용되지 않았지만
       에러가 다른 의미임
       oracle 엔진일 경우 제약 조건을 통해서 db 에러로 날릴수 있고
       mysql 의 경우 java 단에서 5이상은 입력하지 않아서
       dbms 에서 필수 인자가 없다는 메시지를 날릴 것임.


 4) PROPAGATION_REQUIRES_NEW(3)
    : 각각의 처리

 
  
  
 
 
 
  
  
 


서비스에서는 트랜젝션을 걸고 진행시

 @Override
 public void execute(final TicketDto ticketDto) {
//  ticketDto.setAmount("1");
//  ticketDao.buyTicket(ticketDto);
//  ticketDto.setAmount("5");
//  ticketDao.buyTicket(ticketDto); 
  
  
  transactionTemplate2.execute(new TransactionCallbackWithoutResult() {
   
   
   @Override
   protected void doInTransactionWithoutResult(TransactionStatus arg0) {
    ticketDto.setAmount("1");
    ticketDao.buyTicket(ticketDto);
    ticketDto.setAmount("5");
    ticketDao.buyTicket(ticketDto); 
   }
  });
 }


역시 에러는 뜨고  DB에 ② 까지 입력됨 5번 티켓에서걸리면
       해당 트랜젝션 5번 card까지 rollback 함

 5) PROPAGATION_NOT_SUPPORTED(4)
    :  트랜젝션이 없는것과 동일 (정의는 하지만 쓰지 않는다.)
     생략 트랜젝션 적용 전 진행했던 이전 예제를 참고바람

 6) PROPAGATION_NAVER(5) -- 3) 항목처럼 테스트 시 반대로 뜸
    : 트랜젝션에 절대 미 포함(트랜젝션 있는데서 호출시 에러 발생)

 
  
  
 
 
 
  
  
 


서비스(dao 호출클래스) 에서 트랜젝션을 걸고 실행시

Request processing failed; nested exception is org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'

서비스 에서 트랜젝션을 걸지 않고 실행시
   500에러를 떨구지만
    DB에 ③ 까지 입력됨 마지막 하나에서
         에러나고 그 부분만 rollback됨

신입SW인력을 위한 실전 자바(Java) 스프링(Spring) 동영상과정 21_JDBC

이 게시물은
http://www.wiz.center/251

해당 링크를 청취하고 작성한 글입니다.

본인 이해도 확인을 위해서 작성한 것이니
스프링에 대해서 알고 있다면 쑥 훝어보고 끝내시고

좀 깊게 알고 싶다면 위의 링크부터 시작해서
총 30개 강의로 이루어진 스프링 과정을 청취하세요

1. JDBC 이용한 반복코드 줄이기
     ps. 이전 게시물 결과물 에서 작업이 진행될 예정임
   JDBC 연결 순서
    (1) JDBC 드라이버 로드
    (물론 Tomcat 에서 설정되어 있지만 객체는 로드 하고 있음)
    ->(2) DB 연결 -> (3) SQL문 실행 -> (4) DB 연결 해제
   이 작업들이 반복적으로 시행되고 있었음

 1) 설명 : Spring 빈 을 이용해서 위 내용을 간소화 시킬 수 있다.
 2) 방법 : Bean 설정에 JdbcTemplate bean 을 등록하고
               Java 에서 사용한다.
              Datasource bean은 JdbcTemplate 에 포함되어 있다.

 3) 작성순서
  (3-1) pom.xml 에 의존성 추가 할때 참고사항
        maven 은 mysql JDBC를 지원하지 않으므로
        mysql 을 보기위해서는 수기로 다운로드 받아야된다고 한다.
http://hellogk.tistory.com/92


 org.springframework
 spring-jdbc
 4.1.4.RELEASE



ps. 기존에 있는 지 확인하고 넣을 것.

  (3-2) Controller 에 JdcbTemplate 를 멤버변수로 추가
            및 set 함수 추가

......
public JdbcTemplate template;
 
 public void setJdbcTemplate(JdbcTemplate template) {
  this.template= template;
 }
......


  (3-3) servlet-context.xml 에 bean 등록
          JdbcTemplate 가 property로  dataSource 를
         가지고 있는 bean을 등록(여기서는 mysql 용)


    
    
    
    

 

 



  (3-4) controller 의 set 함수에 @Autowired 어노테이션 추가
         bean 등록후 아래와 같이 어노테이션을 추가해야
         JdbcTemplate를 사용 가능하다.
        보통 같으면 bean을 컨트롤러에서 구현하여 사용하는데
        JdbcTemplate는 Spring 에서 구현하고 있기 때문에
        콜 하는 xml 부분과 Controller 에서 셋 하는 부분만 명시하고
      DAO 단에서 사용하면 된다.

......
 @Autowired
 public void setJdbcTemplate(JdbcTemplate template) {
  this.template= template;
 }
......


  (3-5) 사용을 쉽게 하기위해서 util 이라는 패키지를 생성 후
         거기에 다음 내용을 작성한다.

package com.javalec.spring_pjt_board.util;

import org.springframework.jdbc.core.JdbcTemplate;

public class Constant {

 public static JdbcTemplate template;
 
}


  (3-6) controller set 함수를 다시 이렇게 수정한다.
         이후 Constant.template 를 통해 set 한 template를
          어디서든 쓸 수 있게끔 설정되었다.

 @Autowired
 public void setJdbcTemplate(JdbcTemplate template) {
    this.template = template;
    Constant.template = this.template;
 }  


여기까지 진행하면 DAO 을 JDBC 적용할 준비가 끝났다고 본다.
 요약하면 pom.xml 등록하고 -> servlet-context.xml에
    bean 등록
   (dataSource 를 멤버변수로 지니는 template bean 빈 등록)
   -> 컨트롤러에 @Autowired 등록 및 util 클래스 등록
   -> set 함수에 util 선언 template 객체를 등록
   -> 이후 DAO에서 util 객체를 사용하도록 설정하면
   -> 사용준비 끝


 4) 이후 DAO 클래스 작성

  (4-1) DAO 에 멤버변수 JdbcTemplate추가

JdbcTemplate template = null;

  (4-2) 생성자 Constante template 할당

public BDao(){
  
// try {
//  Context context = new InitialContext();
//  datasource = (DataSource) context.lookup("java:comp/env/jdbc/mysql");
//  
//  
// } catch (NamingException e) {
//  e.printStackTrace();
// }
     
          template = Constant.template;
}


  (4-3) list 함수 수정

public ArrayList list(){
//  ArrayList dtos = new ArrayList();
//  
//  Connection connection = null;
//  PreparedStatement preparedStatement = null;
//  ResultSet resultSet = null;
//  
//  try{
//   connection = (Connection) datasource.getConnection();
//   String query = "select bId, bName, bTitle, bContent, bDate, bHit, bGroup, bStep, bIndent from mvc_board order by bGroup desc, bStep asc";
//   preparedStatement = (PreparedStatement) connection.prepareStatement(query);
//   resultSet = preparedStatement.executeQuery();
//   
//   while(resultSet.next()){
//    int bId = resultSet.getInt("bId");
//    String bName = resultSet.getString("bName");
//    String bTitle = resultSet.getString("bTitle");
//    String bContent = resultSet.getString("bContent");
//    Timestamp bDate = resultSet.getTimestamp("bDate");
//    int bHit = resultSet.getInt("bHit");
//    int bGroup = resultSet.getInt("bGroup");
//    int bStep = resultSet.getInt("bStep");
//    int bIndent = resultSet.getInt("bIndent");
//    
//    BDto dto = new BDto(bId, bName, bTitle, bContent, bDate, bHit, bGroup, bStep, bIndent);
//    
//    dtos.add(dto);
//   }
//   
//  }catch(Exception e){
//   e.printStackTrace();
//  }finally{
//   
//    try {
//     if(resultSet != null)resultSet.close();
//     if(preparedStatement != null)preparedStatement.close();
//     if(connection != null)connection.close();
//    } catch (SQLException e2) {
//     // TODO Auto-generated catch block
//     e2.printStackTrace();
//    }
//   
//  }
//  
//  return dtos;
  
  String query = "select bId, bName, bTitle, bContent, bDate, bHit, bGroup, bStep, bIndent from mvc_board order by bGroup desc, bStep asc";
  return (ArrayList) template.query(query, new BeanPropertyRowMapper(BDto.class));
 }


  (4-4) write 함수 수정 - 이전 소스 생략
      ps. mysql 에서는 oracle 과는 다르게 update 문의 서브쿼리에
          같은 테이블에서 select 한 결과를 넣지 못하도록 설정되어 있다.
          그래서 from tablename a 정도로 테이블 명을 바꿔주면
          다른 테이블로 인식하게끔 작성해서 진행하는건 가능하다.

public void write(final String bName, final String bTitle, final String bContent){

 template.update(new PreparedStatementCreator() {
   @Override
  public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
   String query2 ="insert into test.mvc_board (bName, bTitle, bContent, bDate, bHit, bGroup, bStep, bIndent) "
     + "        values (?, ?, ?, now(), 0, (select max(bId) as bGroup from mvc_board a), 0, 0)";
   PreparedStatement psmt = con.prepareStatement(query2);
   psmt.setString(1, bName );
   psmt.setString(2, bTitle );
   psmt.setString(3, bContent );
//   String query = "select max(bId) as maxId from mvc_board";
//   JdbcTemplate template = Constant.template;
//   psmt.setInt(4, template.query(query, Integer);
   return psmt;
  }
 });
}


  (4-5) upHit 함수 수정

private void upHit( final String bId) {
  String query = "update test.mvc_board set bHit = bHit + 1 where bId = ?";
  template.update(query, new PreparedStatementSetter() {
   
   @Override
   public void setValues(PreparedStatement ps) throws SQLException {
    ps.setInt(1, Integer.parseInt(bId));
   }
  });
 }


  (4-6) modify()

 public void modify(final String bId, final String bName, final String bTitle, final String bContent){
  String query = "update mvc_board set bName =?, bTitle=?, bContent=? where bId = "+?;
  template.update(query, new PreparedStatementSetter() {
   
   @Override
   public void setValues(PreparedStatement ps) throws SQLException {
    ps.setString(1, bId);
    ps.setString(2, bName);
    ps.setString(3, bTitle);
    ps.setInt(4, Integer.parseInt(bContent));
   }
  });
 }


  (4-7) delete()

 public void delete(final String bId) {
  String query = "delete from mvc_board where bId = ?";
  
  template.update(query, new PreparedStatementSetter() {
   
   @Override
   public void setValues(PreparedStatement ps) throws SQLException {
    ps.setString(1, bId);
   }
  });
 }


  (4-8) reply_view()

public BDto reply_view(String bid) {
  String query = "select * from mvc_board where bId = "+bid;
  return template.queryForObject(query, new BeanPropertyRowMapper(BDto.class));
}


  (4-9) reply()

public void reply(final String bId, final String bName, final String bTitle, final String bContent, final String bGroup, final String bStep, final String bIndent) {
  String query = "insert into test.mvc_board(bName, bTitle, bContent, bDate, bHit, bGroup, bStep, bIndent) values (?,?,?,now(),0,?,?,?)";
  template.update(query, new PreparedStatementSetter() {
   
   @Override
   public void setValues(PreparedStatement ps) throws SQLException {
    ps.setString(1,bName);
    ps.setString(2,bTitle);
    ps.setString(3,bContent);
    ps.setInt(4,Integer.parseInt(bGroup));
    ps.setInt(5,Integer.parseInt(bStep)+1);
    ps.setInt(6,Integer.parseInt(bIndent)+1);
   }
  });
}


  (4-10) replyShape()

 private void replyShape( final String strGroup, final String strStep) {
  String query = "update mvc_board set bStep = bStep + 1 where bGroup = ? and bStep > ?";
  template.update(query, new PreparedStatementSetter() {
   
   @Override
   public void setValues(PreparedStatement ps) throws SQLException {
    ps.setInt(1, Integer.parseInt(strGroup));
    ps.setInt(2, Integer.parseInt(strStep));
   }
  });
 }


  (4-11) contentView()

 upHit(strid);
 String query = "select bId, bName, bTitle, bContent, bDate, bHit, bGroup, bStep, bIndent from mvc_board where bId="+strid;
 return template.queryForObject(query, new BeanPropertyRowMapper(BDto.class));


2015년 7월 28일 화요일

신입SW인력을 위한 실전 자바(Java) 스프링(Spring) 동영상과정 16~21 게시판 작성하기

이 게시물은
http://www.wiz.center/246
http://www.wiz.center/247
http://www.wiz.center/248
http://www.wiz.center/249
http://www.wiz.center/250

해당 링크를 청취하고 작성한 글입니다.

본인 이해도 확인을 위해서 작성한 것이니
스프링에 대해서 알고 있다면 쑥 훝어보고 끝내시고

좀 깊게 알고 싶다면 위의 링크부터 시작해서
총 30개 강의로 이루어진 스프링 과정을 청취하세요

1. 게시판 설계
   기본 진행 혹은 db 작업을 요청한 경우
         : 사용자요청 -> dispatcher-> Controller
            -> command(s) -> DAo -> DB
   DB작업 후 혹은 db를 거치지 않는 요청
         : Controller -> view(s) (jsp 파일)
      * 데이터 객체는 DTO로 작성

2. DB 작성
   예제는 Oracle 테이블 구축이지만 여기서는 Mysql 로 작성
   Mysql 설치방법은 Oracle.com 에서 받는데 이중 mysqld 로
    프로세스만 띄워서 진행하는 방법으로 (pc에 주는 부하가 싫어서)
    진행함.
   테이블 작성의 경우 Oracle과 다른점은 크게 없지만

   bId 의 경우 AUTO_INCREMENT 설정을 줘서
                  자동으로 번호 부여가 가능하다

   varchar2 -> varchar

   sysdate -> current_timestamp or now() or 트리거 사용
                  으로 설정 가능 버전마다 상이함

  CHARSET=utf8 인데 세부 옵션은 utf8_general_ci

CREATE TABLE IF NOT EXISTS `mvc_board` (
  `bId` int(4) NOT NULL AUTO_INCREMENT,
  `bName` varchar(20) CHARACTER SET latin1 DEFAULT NULL,
  `bTitle` varchar(100) CHARACTER SET latin1 DEFAULT NULL,
  `bContent` varchar(300) CHARACTER SET latin1 DEFAULT NULL,
  `bDate` datetime DEFAULT CURRENT_TIMESTAMP,
  `bHit` int(4) DEFAULT '0',
  `bGroup` int(4) DEFAULT NULL,
  `bStep` int(4) DEFAULT NULL,
  `bIndent` int(4) DEFAULT NULL,
  PRIMARY KEY (`bId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;


복습 : 계층형 게시판 참고사항

 bGroup <> 로 표시함
   게시판 원글부터 시작하여 같은 글에 포함된 댓글 모두는
    같은 번호를 가지게 된다.
    원글 <1>
   └답변 <1>
   └답변 <1>
      └답변 <1>
   └답변 <1>
    원글 <2>
    └답변 <2>
       └답변 <2>

 bStep ()로 표시
   몇번째 글인지를 표시
    원글 <1> (1)
   └답변 <1> (2)
   └답변 <1> (3)
      └답변 <1> (4)
   └답변 <1> (5)
    원글 <2> (1)
    └답변 <2> (2)
       └답변 <2> (3)

 bIndent []로 표시
   위에서 몇번째 들여쓰기 대상인가 (원본글의 댓글인가 댓글의 댓글인가)
    원글 <1> (1) [1]
   └답변 <1> (2) [2]
   └답변 <1> (3) [2]
      └답변 <1> (4) [3]
   └답변 <1> (5) [2]
    원글 <2> (1) [1]
    └답변 <2> (2) [2]
       └답변 <2> (3) [3]


3. 프로젝트 생성
 1) spring Project - spring MVC Project 로 생성
    프로젝트명은 :  spring_pjt_board
    패키지는 :  com.javalec.spring_pjt_board

 2) web.xml 에 다음 내용을 추가(한글 깨짐 방지)


 encodingFilter
 org.springframework.web.filter.CharacterEncodingFilter

 
  encoding
  UTF-8
 



 encodingFilter
 /*



3. list 페이지 작성
 1) 패키지 및 클래스 생성
   

    ps. 이때 BCommand 는
               void execute(Model model);
         함수를 가지는 interface고
         나머지는 Command를 implements 할 것.

 2) 컨트롤러를 인식하기 위한 servlet 셋팅
     servlet-context.xml 의 context:component-scan base-package
      를 아래와 같이 변경




 3) 컨트롤러 작성 및 메소드별 유형
   (3-1) 단순한 페이지 요청
     - 작업 화면 지시

 @RequestMapping("/write_view")
 public String write_view(Model model){
  System.out.println("write_view()");
  //작성화면 호출이라 입력할수 있는 화면으로 이동만 담당
  return "write_view";
 }


   (3-2) DB에서 자료를 가지고 화면에 보여줘야 되는 경우
     - list

 @RequestMapping("/list")
 public String like(Model model){
  System.out.println("list()");
  command = new BListCommand();
  command.execute(model);
  
  return "list";
 }


   (3-3) form 객체를 받아 처리해야하는 경우
     - 수정, 변경 작업 진행시

 @RequestMapping("/content_view")
 public String content_view(HttpServletRequest request, Model model){
  System.out.println("content_view()");
  
  model.addAttribute("request", request);
  command = new BContentCommand();
  command.execute(model);
  
  return "content_view";
 }


작업 환경에 따라 변경 적용 하여 진행

package com.javalec.spring_pjt_board.controller;

import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.javalec.spring_pjt_board.command.BCommand;
import com.javalec.spring_pjt_board.command.BContentCommand;
import com.javalec.spring_pjt_board.command.BDeleteCommand;
import com.javalec.spring_pjt_board.command.BListCommand;
import com.javalec.spring_pjt_board.command.BModifyCommand;
import com.javalec.spring_pjt_board.command.BReplyCommand;
import com.javalec.spring_pjt_board.command.BReplyViewCommand;
import com.javalec.spring_pjt_board.command.BWriteCommand;

@Controller
public class BController {
 BCommand command;
 
 @RequestMapping("/list")
 public String like(Model model){
  System.out.println("list()");
  command = new BListCommand();
  command.execute(model);
  
  return "list";
 }
 
 @RequestMapping("/write_view")
 public String write_view(Model model){
  System.out.println("write_view()");
  //작성화면 호출이라 입력할수 있는 화면으로 이동만 담당
  return "write_view";
 }
 
 @RequestMapping("/write")
 public String write(HttpServletRequest request, Model model){
  System.out.println("write()");
  model.addAttribute("request", request);
  command = new BWriteCommand();
  command.execute(model);
  
  return "redirect:list";
 }
 
 @RequestMapping("/content_view")
 public String content_view(HttpServletRequest request, Model model){
  System.out.println("content_view()");
  
  model.addAttribute("request", request);
  command = new BContentCommand();
  command.execute(model);
  
  return "content_view";
 }
 
 @RequestMapping(method=RequestMethod.POST, value="/modify")
 public String modify(HttpServletRequest request, Model model){
  System.out.println("modify");
  
  model.addAttribute("request", request);
  command = new BModifyCommand();
  command.execute(model);
  
  return "redirect:list";
 }
 
 @RequestMapping("/reply_view")
 public String reply_view(HttpServletRequest request, Model model){
  System.out.println("reply_view()");
  
  model.addAttribute("request", request);
  command = new BReplyViewCommand();
  command.execute(model);
  
  return "reply_view";
 }
 
 @RequestMapping("/reply")
 public String reply(HttpServletRequest request, Model model){
  System.out.println("reply()");
  
  model.addAttribute("request", request);
  command = new BReplyCommand();
  command.execute(model);
  
  return "redirect:list";
 }
 @RequestMapping("/delete")
 public String delete(HttpServletRequest request, Model model){
  System.out.println("delete()");
  
  model.addAttribute("request", request);
  command = new BDeleteCommand();
  command.execute(model);
  
  return "redirect:list";
 }
}


 3) DTO 객체 작성
    - 테이블 구조와 같은 멤버변수를 지니도록 작성
      , set 및 get 함수를 생성
      , list() 라는 함수를 만들어 각 멤버변수가 들어가도록 생성

package com.javalec.spring_pjt_board.dto;

import java.sql.Timestamp;

public class BDto {
 int bId;
 String bName;
 String bTitle;
 String bContent;
 Timestamp bDate;
 int bHit;
 int bGroup;
 int bStep;
 int bIndent;
 
 public BDto(){
  
 }
 
 public BDto(int bId, String bName, String bTitle, String bContent, Timestamp bDate, int bHit, int bGroup, int bStep, int bIndent){
  this.bId = bId;
  this.bName = bName;
  this.bTitle = bTitle;
  this.bContent = bContent;
  this.bDate = bDate;
  this.bHit = bHit;
  this.bGroup = bGroup;
  this.bStep = bStep;
  this.bIndent = bIndent;
    
 }
 
 public String getbContent() {
  return bContent;
 }

 public void setbContent(String bContent) {
  this.bContent = bContent;
 }

 

 public int getbId() {
  return bId;
 }

 public void setbId(int bId) {
  this.bId = bId;
 }

 public String getbName() {
  return bName;
 }

 public void setbName(String bName) {
  this.bName = bName;
 }

 public String getbTitle() {
  return bTitle;
 }

 public void setbTitle(String bTitle) {
  this.bTitle = bTitle;
 }

 public Timestamp getbDate() {
  return bDate;
 }

 public void setbDate(Timestamp bDate) {
  this.bDate = bDate;
 }

 public int getbHit() {
  return bHit;
 }

 public void setbHit(int bHit) {
  this.bHit = bHit;
 }

 public int getbGroup() {
  return bGroup;
 }

 public void setbGroup(int bGroup) {
  this.bGroup = bGroup;
 }

 public int getbStep() {
  return bStep;
 }

 public void setbStep(int bStep) {
  this.bStep = bStep;
 }

 public int getbIndent() {
  return bIndent;
 }

 public void setbIndent(int bIndent) {
  this.bIndent = bIndent;
 }
}


3-1) DB설정을 Mysql로 하기위해 다음 작업을 추가
    아래 링크 참고하여 작성
    http://www.mkyong.com/tomcat/how-to-configure-mysql-datasource-in-tomcat-6/


 4) DAO 작성 
    * query 작성시 주의
    * 현재는 내용대로 불편한 방식으로 작성 후 
        차후 좀더 편한방식
        (mybatis, ibatis, xml) 이나 
        사용목적에 따라 (ajax 등)
        으로 변경예정

package com.javalec.spring_pjt_board.dao;

import java.sql.Connection; 
import java.sql.PreparedStatement;
import java.sql.ResultSet; 
           //mysql 에서도 같은걸 제공하는데 그냥 sql꺼 쓸것.
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;

import javax.annotation.Resource;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

import com.javalec.spring_pjt_board.dto.BDto;

public class BDao {
 
 @Resource(name="jdbc/mysql")
 DataSource datasource;
 
 public BDao(){
  try {
   Context context = new InitialContext();
   datasource = (DataSource) context.lookup("java:comp/env/jdbc/mysql");
   
   
  } catch (NamingException e) {
   e.printStackTrace();
  }
 }
 
 public ArrayList list(){
  ArrayList dtos = new ArrayList();
  
  Connection connection = null;
  PreparedStatement preparedStatement = null;
  ResultSet resultSet = null;
  
  try{
   connection = (Connection) datasource.getConnection();
   String query = "select bId, bName, bTitle, bContent, bDate, bHit, bGroup, bStep, bIndent from mvc_board order by bGroup desc, bStep asc";
   preparedStatement = (PreparedStatement) connection.prepareStatement(query);
   resultSet = preparedStatement.executeQuery();
   
   while(resultSet.next()){
    int bId = resultSet.getInt("bId");
    String bName = resultSet.getString("bName");
    String bTitle = resultSet.getString("bTitle");
    String bContent = resultSet.getString("bContent");
    Timestamp bDate = resultSet.getTimestamp("bDate");
    int bHit = resultSet.getInt("bHit");
    int bGroup = resultSet.getInt("bGroup");
    int bStep = resultSet.getInt("bStep");
    int bIndent = resultSet.getInt("bIndent");
    
    BDto dto = new BDto(bId, bName, bTitle, bContent, bDate, bHit, bGroup, bStep, bIndent);
    
    dtos.add(dto);
   }
   
  }catch(Exception e){
   e.printStackTrace();
  }finally{
   
    try {
     if(resultSet != null)resultSet.close();
     if(preparedStatement != null)preparedStatement.close();
     if(connection != null)connection.close();
    } catch (SQLException e2) {
     // TODO Auto-generated catch block
     e2.printStackTrace();
    }
   
  }
  
  return dtos;
 }
}


 5) list를 가져올 list.jsp 작성
      (jstl + jsp 문법을 알고 진행)

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<table width="500" cellpadding="0" cellspacing="0" border="1">
<tr>
<td>번호</td>
<td>이름</td>
<td>제목</td>
<td>날짜</td>
<td>히트</td>
</tr>
<c:forEach items="${list}" var="dto">
<tr>
<td>${dto.bId}</td>
<td>${dto.bName}</td>
<td>
<c:forEach begin="1" end="${dto.bIndent}">-</c:forEach>
<a href="content_view?bId=${dto.bId}">${dto.bTitle}</a></td>
<td>${dto.bDate}</td>
<td>${dto.bHit}</td>
</tr>
</c:forEach>
<tr>
<td colspan="5"> <a href="write_view">글작성</a> </td>
</tr>
</table>
</body>
</html>


6) 실행


4. write 작성
 1) write_view.jsp 작성
    controller 에 아래와 같이 작성되어 있는 상태인데

 @RequestMapping("/write_view")
 public String write_view(Model model){
  System.out.println("write_view()");
  //작성화면 호출이라 입력할수 있는 화면으로 이동만 담당
  return "write_view";
 }


이중 앞에 건 그대로 페이지만 이동하게끔 하면 됨
   이에 따라서 write_view.jsp 를 아래와 같이 작성한다.

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

<table width="500" cellpadding="0" cellspacing="0" border="1">
<form action="write" method="post">
<tr>
<td> 이름 </td>
<td> <input type="text" name="bName" size = "50"> </td>
</tr>
<tr>
<td> 제목 </td>
<td> <input type="text" name="bTitle" size = "50"> </td>
</tr>
<tr>
<td> 내용 </td>
<td> <textarea name="bContent" rows="10" ></textarea> </td>
</tr>
<tr >
<td colspan="2"> <input type="submit" value="입력"> &nbsp;&nbsp; <a href="list.do">목록보기</a></td>
</tr>
</form>
</table>
</body>
</html>

 2) write 라는 요청에 대해 대응 하기 위한 컨트롤러 작성 내용 확인

 @RequestMapping("/write")
 public String write(HttpServletRequest request, Model model){
  System.out.println("write()");
  model.addAttribute("request", request);
  command = new BWriteCommand();
  command.execute(model);
  
  return "redirect:list";
 }


    그리고 
     BWriteCommand() 클래스 작성

package com.javalec.spring_pjt_board.command;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.ui.Model;

import com.javalec.spring_pjt_board.dao.BDao;

public class BWriteCommand implements BCommand {

 @Override
 public void execute(Model model) {
  // TODO Auto-generated method stub
  Map map = model.asMap();
  HttpServletRequest request = (HttpServletRequest) map.get("request");
  
  String bName = request.getParameter("bName");
  String bTitle = request.getParameter("bTitle");
  String bContent = request.getParameter("bContent");
  
  BDao dao = new BDao();
  
  dao.write(bName, bTitle, bContent);
  
 }

}


    여기에서 호출하는  DAO 의 함수 작성
     mysql에서는 시퀀스 증가값을 테이블에 하나씩 밖에 줄수 없기 
     때문에 id max 값을 가져와서 group 를 짓도록 작성함
     그냥 조회해서 write 했다고 생각하면 됨.

 
 public void write(String bName, String bTitle, String bContent){
  Connection connection = null;
  PreparedStatement preparedStatement = null;
  ResultSet resultSet = null;
  //mysql 추가분 group 의 경우 id와 같은 값이지만 (글 작성시)
  // mysql에는 증가값이 테이블당 1개만 작성가능해서 따로 구해서 insert를 진행하기로 함
  // 해당 예제는 처음 작성시에 대한 대응이 안되어 있음.
  
  int maxid=0;
  try{
   connection = (Connection) datasource.getConnection();
   String query = "select max(bId) as maxId from mvc_board";
   preparedStatement = (PreparedStatement) connection.prepareStatement(query);
   resultSet = preparedStatement.executeQuery();
   
   if(resultSet.next()){
    maxid = resultSet.getInt("maxId");
   }
   
  }catch(Exception e){
   e.printStackTrace();
  }finally{
   
    try {
     if(resultSet != null)resultSet.close();
     if(preparedStatement != null)preparedStatement.close();
     if(connection != null)connection.close();
    } catch (SQLException e2) {
     // TODO Auto-generated catch block
     e2.printStackTrace();
    }
  }
  
  try{
   connection = datasource.getConnection();
   String query ="insert into test.mvc_board (bName, bTitle, bContent, bDate, bHit, bGroup, bStep, bIndent) "
     + "        values (?,?,?,now(),0,? ,0,0)";
      
   preparedStatement = connection.prepareStatement(query);
   preparedStatement.setString(1, bName);
   preparedStatement.setString(2, bTitle);
   preparedStatement.setString(3, bContent);
   preparedStatement.setInt(4, maxid);
   int rm = preparedStatement.executeUpdate();
   System.out.println("insert number :"+rm);
  }catch(Exception e){
   e.printStackTrace();
  }finally{
   try{
    if(preparedStatement != null)preparedStatement.close();
    if(connection != null)connection.close();
   }catch(Exception e2){
    e2.printStackTrace();
   }
  }
 }

5. 게시글 보기 작성
 1) 게시글에 대한 content_view.jsp 파일 작성
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

<table width="500" cellpadding="0" cellspacing="0" border="1">
<form action="modify" method="post">
<input type="hidden" name="bId" value="${content_view.bId}">
<tr>
<td> 번호 </td>
<td> ${content_view.bId} </td>
</tr>
<tr>
<td> 히트 </td>
<td> ${content_view.bHit} </td>
</tr>
<tr>
<td> 이름 </td>
<td> <input type="text" name="bName" value="${content_view.bName}"></td>
</tr>
<tr>
<td> 제목 </td>
<td> <input type="text" name="bTitle" value="${content_view.bTitle}"></td>
</tr>
<tr>
<td> 내용 </td>
<td> <textarea rows="10" name="bContent" >${content_view.bContent}</textarea></td>
</tr>
<tr >
<td colspan="2"> <input type="submit" value="수정"> &nbsp;&nbsp; <a href="list">목록보기</a> &nbsp;&nbsp; <a href="delete?bId=${content_view.bId}">삭제</a> &nbsp;&nbsp; <a href="reply_view?bId=${content_view.bId}">답변</a></td>
</tr>
</form>
</table>
</body>
</html>


 2) 컨트롤러에서는 BContentCommand() 클래스에서 
     작성토록 설정하였기 때문에 해당 클래스를 작성한다.

 @Override
 public void execute(Model model) {
  // TODO Auto-generated method stub
  Map map = model.asMap();
  HttpServletRequest request = (HttpServletRequest) map.get("request");
  String bId = request.getParameter("bId");
  // after write
  BDao dao = new BDao();
  BDto dto = dao.contentView(bId);
  
  model.addAttribute("content_view", dto);
 }


 3) BContentCommand() 에서 호출하는 dao.contentView(bId)
       를 구현한다.
       Content 가져올때 hit 숫자를 증가시키는 메소드를 포함하였음.

public BDto contentView(String strid){
  upHit(strid);
  
  BDto dto = null;
  
  Connection connection = null;
  PreparedStatement preparedStatement = null;
  ResultSet resultSet = null;
  
  try{
   connection = (Connection) datasource.getConnection();
   
   String query = "select bId, bName, bTitle, bContent, bDate, bHit, bGroup, bStep, bIndent from mvc_board where bId=?";
   
   preparedStatement = (PreparedStatement) connection.prepareStatement(query);
   preparedStatement.setInt(1, Integer.parseInt(strid));
   resultSet = preparedStatement.executeQuery();
   
   if(resultSet.next()){
    int bId = resultSet.getInt("bId");
    String bName = resultSet.getString("bName");
    String bTitle = resultSet.getString("bTitle");
    String bContent = resultSet.getString("bContent");
    Timestamp bDate = resultSet.getTimestamp("bDate");
    int bHit = resultSet.getInt("bHit");
    int bGroup = resultSet.getInt("bGroup");
    int bStep = resultSet.getInt("bStep");
    int bIndent = resultSet.getInt("bIndent");
    dto = new BDto(bId, bName, bTitle, bContent, bDate, bHit, bGroup, bStep, bIndent);
    
   }
   
  }catch(Exception e){
   e.printStackTrace();
  }finally{
   try{
   if(resultSet != null)resultSet.close();
   if(preparedStatement != null)preparedStatement.close();
   if(connection != null)connection.close();
   }catch(Exception e2){
    e2.printStackTrace();
   }
  }
  System.out.println(dto.toString());
  return dto;
 }

  private void upHit( String bId) {
  // TODO Auto-generated method stub
  Connection connection = null;
  PreparedStatement preparedStatement = null;
  
  try {
   connection = (Connection) datasource.getConnection();
   String query = "update test.mvc_board set bHit = bHit + 1 where bId = ?";
   preparedStatement = connection.prepareStatement(query);
   preparedStatement.setString(1, bId);
   
   int rn = preparedStatement.executeUpdate();
     
  } catch (Exception e) {
   // TODO: handle exception
   e.printStackTrace();
  } finally {
   try {
    if(preparedStatement != null) preparedStatement.close();
    if(connection != null) connection.close();
   } catch (Exception e2) {
    // TODO: handle exception
    e2.printStackTrace();
   }
  }
 }


6. 글 수정 하기
 1) jsp는 content_view.jsp 에서 보내는 form 을 확인
      modify - POST 방식으로 보내는걸 확인 가능함


 2) BCommand 인터페이스를 implements 한 BModifyCommand
     를 작성한다.

package com.javalec.spring_pjt_board.command;

import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.ui.Model;

import com.javalec.spring_pjt_board.dao.BDao;
import com.javalec.spring_pjt_board.dto.BDto;

public class BModifyCommand implements BCommand {

 @Override
 public void execute(Model model) {
  // TODO Auto-generated method stub
  Map map = model.asMap();
  HttpServletRequest request = (HttpServletRequest) map.get("request");
  
  String bId = request.getParameter("bId");
  String bName = request.getParameter("bName");
  String bTitle = request.getParameter("bTitle");
  String bContent = request.getParameter("bContent");
  
  BDao dao = new BDao();
  dao.modify(bId, bName, bTitle, bContent);
 }
}


 3) dao 에 트렌젝션 처리를 위한 작업을 진행한다.

public void modify(String bId, String bName, String bTitle, String bContent){
  Connection connection = null;
  PreparedStatement preparedStatement = null;
  
  try{
   connection = (Connection) datasource.getConnection();
   
   String query = "update mvc_board set bName =?, bTitle=?, bContent=? where bId = ?";
   
   preparedStatement = (PreparedStatement) connection.prepareStatement(query);
   
   preparedStatement.setString(1, bName);
   preparedStatement.setString(2, bTitle);
   preparedStatement.setString(3, bContent);
   preparedStatement.setInt(4,Integer.parseInt(bId));
   
   int rn=preparedStatement.executeUpdate();
   
  }catch(Exception e){
   e.printStackTrace();
  }finally{
   try{
   if(preparedStatement != null)preparedStatement.close();
   if(connection != null)connection.close();
   }catch(Exception e2){
    e2.printStackTrace();
   }
  }
 }


7. 글 삭제 하기
 1) Controller 는 그대로 진행

 2) BModifyCommand .java 작성

package com.javalec.spring_pjt_board.command;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.ui.Model;

import com.javalec.spring_pjt_board.dao.BDao;
import com.javalec.spring_pjt_board.dto.BDto;

public class BModifyCommand implements BCommand {

 @Override
 public void execute(Model model) {
  // TODO Auto-generated method stub
  Map map = model.asMap();
  HttpServletRequest request = (HttpServletRequest) map.get("request");
  
  String bId = request.getParameter("bId");
  String bName = request.getParameter("bName");
  String bTitle = request.getParameter("bTitle");
  String bContent = request.getParameter("bContent");
  
  BDao dao = new BDao();
  dao.modify(bId, bName, bTitle, bContent);

 }
}


 3) Dao 에 delete 함수 작성

public void delete(String bId) {
  Connection connection = null;
  PreparedStatement preparedStatement = null;
  
  try{
   connection = (Connection) datasource.getConnection();
   
   String query = "delete from mvc_board where bId = ?";
   
   preparedStatement = (PreparedStatement) connection.prepareStatement(query);
   
   preparedStatement.setInt(1,Integer.parseInt(bId));
   
   int rn=preparedStatement.executeUpdate();
   
  }catch(Exception e){
   e.printStackTrace();
  }finally{
   try{
   if(preparedStatement != null)preparedStatement.close();
   if(connection != null)connection.close();
   }catch(Exception e2){
    e2.printStackTrace();
   }
  }
 }


8. 글 답변 페이지 만들기

1) 글에서 답변을 눌렀을때 뜨는 화면인 reply_view.jsp를
    아래와 같이 작성

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

<table width="500" cellpadding="0" cellspacing="0" border="1">
<form action="reply" method="post">
<input type="hidden" name="bId" value="${reply_view.bId}">
<input type="hidden" name="bGroup" value="${reply_view.bGroup}">
<input type="hidden" name="bStep" value="${reply_view.bStep}">
<input type="hidden" name="bIndent" value="${reply_view.bIndent}">
<tr>
<td> 번호 </td>
<td> ${reply_view.bId} </td>
</tr>
<tr>
<td> 히트 </td>
<td> ${reply_view.bHit} </td>
</tr>
<tr>
<td> 이름 </td>
<td> <input type="text" name="bName" value="${reply_view.bName}"></td>
</tr>
<tr>
<td> 제목 </td>
<td> <input type="text" name="bTitle" value="${reply_view.bTitle}"></td>
</tr>
<tr>
<td> 내용 </td>
<td> <textarea rows="10"  name="bContent">${reply_view.bContent}</textarea></td>
</tr>
<tr >
<td colspan="2"><input type="submit" value="답변"> <a href="list" >목록</a></td>
</tr>
</form>
</table>

</body>
</html>


2) 컨트롤러에서 해당 내용 확인 및 ReplyViewCommand.java 구현

package com.javalec.spring_pjt_board.command;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.ui.Model;

import com.javalec.spring_pjt_board.dao.BDao;
import com.javalec.spring_pjt_board.dto.BDto;

public class BReplyViewCommand implements BCommand {

 @Override
 public void execute(Model model) {
  Map map = model.asMap();
  HttpServletRequest request = (HttpServletRequest) map.get("request");
  
  String bId = request.getParameter("bId");
  
  BDao dao = new BDao();
  BDto dto = dao.reply_view(bId);
  
  model.addAttribute("replay_view", dto);
 }
}


3) dto.reply_view(bID) 구현

 public BDto reply_view(String bid) {
  BDto dto = new BDto();
  
  Connection connection = null;
  PreparedStatement preparedStatement = null;
  ResultSet resultSet = null;
  
  
  try{
   connection = (Connection) datasource.getConnection();
   
   String query = "select * from mvc_board where bId = ?";
   
   preparedStatement = (PreparedStatement) connection.prepareStatement(query);
   preparedStatement.setInt(1,Integer.parseInt(bid));
   
   resultSet = preparedStatement.executeQuery();
   
   if(resultSet.next()){
    int bId = resultSet.getInt("bId");
    String bName = resultSet.getString("bName");
    String bTitle = resultSet.getString("bTitle");
    String bContent = resultSet.getString("bContent");
    Timestamp bDate = resultSet.getTimestamp("bDate");
    int bHit = resultSet.getInt("bHit");
    int bGroup = resultSet.getInt("bGroup");
    int bStep = resultSet.getInt("bStep");
    int bIndent = resultSet.getInt("bIndent");
    
    dto = new BDto(bId, bName, bTitle, bContent, bDate, bHit, bGroup, bStep, bIndent);
    
   }
  }catch(Exception e){
   e.printStackTrace();
  }finally{
   try{
   if(resultSet != null)connection.close();
   if(preparedStatement != null)preparedStatement.close();
   if(connection != null)connection.close();
   
   }catch(Exception e2){
    e2.printStackTrace();
   }
  }
  
  return dto;
 }


4) BReplyCommand.java 작성

package com.javalec.spring_pjt_board.command;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.ui.Model;

import com.javalec.spring_pjt_board.dao.BDao;

public class BReplyCommand implements BCommand {

 @Override
 public void execute(Model model) {
  Map map = model.asMap();
  HttpServletRequest request = (HttpServletRequest) map.get("request");
  
  String bId =  request.getParameter("bId");
  String bName =  request.getParameter("bName");
  String bTitle =  request.getParameter("bTitle");
  String bContent =  request.getParameter("bContent");
  String bGroup =  request.getParameter("bGroup");
  String bStep =  request.getParameter("bStep");
  String bIndent =  request.getParameter("bIndent");
  
  BDao dao = new BDao();
  dao.reply(bId, bName, bTitle, bContent, bGroup, bStep, bIndent);
 }

}


5) dao 에 다음 두개 함수를 추가

 public void reply(String bId, String bName, String bTitle, String bContent, String bGroup, String bStep, String bIndent) {
  replyShape(bGroup, bStep);
  
  Connection connection = null;
  PreparedStatement preparedStatement = null;
  
  try{
   connection = (Connection) datasource.getConnection();
   
   String query = "insert into test.mvc_board(bName, bTitle, bContent, bDate, bHit, bGroup, bStep, bIndent) values (?,?,?,now(),0,?,?,?)";
   
   preparedStatement = (PreparedStatement) connection.prepareStatement(query);
   
   preparedStatement.setString(1,bName);
   preparedStatement.setString(2,bTitle);
   preparedStatement.setString(3,bContent);
   preparedStatement.setInt(4,Integer.parseInt(bGroup));
   preparedStatement.setInt(5,Integer.parseInt(bStep)+1);
   preparedStatement.setInt(6,Integer.parseInt(bIndent)+1);
   
   int rn=preparedStatement.executeUpdate();
   
  }catch(Exception e){
   e.printStackTrace();
  }finally{
   try{
   if(preparedStatement != null)preparedStatement.close();
   if(connection != null)connection.close();
   }catch(Exception e2){
    e2.printStackTrace();
   }
  }
 }
 

 private void replyShape( String strGroup, String strStep) {
  // TODO Auto-generated method stub
  Connection connection = null;
  PreparedStatement preparedStatement = null;
  
  try {
   connection = datasource.getConnection();
   String query = "update mvc_board set bStep = bStep + 1 where bGroup = ? and bStep > ?";
   preparedStatement = connection.prepareStatement(query);
   preparedStatement.setInt(1, Integer.parseInt(strGroup));
   preparedStatement.setInt(2, Integer.parseInt(strStep));
   
   int rn = preparedStatement.executeUpdate();
  } catch (Exception e) {
   // TODO: handle exception
   e.printStackTrace();
  } finally {
   try {
    if(preparedStatement != null) preparedStatement.close();
    if(connection != null) connection.close();
   } catch (Exception e2) {
    // TODO: handle exception
    e2.printStackTrace();
   }
  }
 }


9. 게시판 작성중 한글 처리 관련 오류 수정 방법
    자세한 방법은 아래 링크를 참고할 것.
http://blog.naver.com/akrmak6/203238987

     포인트는 db 설정을 바꿈 -> 컬럼, 테이블 설정 문자셋을 교체
       (컬럼별로도로 문자셋이 있음) -> mysql 재시작
      ok
  Oracle 로 하면 잘되는데 Mysql, MariaDB 두가지는
      위처럼 set 별, table, 컬럼별 설정이 동일해야
      정상적으로 한글을 표현 할 수 있다.
     그렇지만 결론은 플잭 초기에 확인되기 때문에 이것때문에
      고생할 일은 없다.