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, 컬럼별 설정이 동일해야
      정상적으로 한글을 표현 할 수 있다.
     그렇지만 결론은 플잭 초기에 확인되기 때문에 이것때문에
      고생할 일은 없다.

2015년 7월 26일 일요일

리눅스 서버 알아보기_04. 압축 및 묶음

보통 프로그램 설치시
     -> 소스를 공개함
     -> 사용자는 소스를 컴파일 하여 사용
     -> 문제점이 발생) 파일과 디렉토리가 많음
     -> 어떤 방식으로 공개할지가 고민되었던 문제
    --> ~~.tar or ~~.gz(소스)
          1.압축풀고, 2묶음풀고, 3소스 컴파일, 4사용
    --> 최근에는 rpm 이라는 압축파일로 사용
           윈도우처럼 1~4를 진행 가능
         그렇지만 전통적인 방법도 계속 사용중임

1. 압축
기본 명령어
    gzip (~~~.gz)
      /root에서
      ls -alh
      gzip install.log
      ls -alh
       순으로 명령 진행
         --> 1. 용량이 적어진걸 확인 가능
              2. 기존 파일은 사라짐
              3. 파일위에 .zip이 붙은 파일을 보여준다.

    gunzip
      gunzip install.log
      ls -alh
         ---> 1. 압축이 풀림, 용량이 기존대로 돌아옴.
               2. .zip이 사라짐

    ps. gzip -1v -9v 까지 압축률에 대한 옵션 지정가능
        그러나 압축률에 크게 차이는 나지 않는다고 한다.
          gzip -1v install.log
          gunzip install.log
          gzip -9v install.log

다른 방식의 압축 지원 방식
    bzip2 (~~~.bz9)
      /root에서
       ls -alh
       bzip2 install.log
       ls -alh
         --> unzip와 같은데 확장자가 bz2로 남고
               압축률에 차이가 있음

    bunzip2
      gunzip install.log.bz2
      ls -alh
         --> <bunzip은 .bz2까지 명시해줄것>
              사용시 압축 풀림

RedHot에서는 설치되어 있지 않지만 쓰이는 압축프로그램
       사용방법은 동일하므로 생략함
    compress (~~~~.Z)

    uncompress

--------------------------------------
2. 묶음 (사이즈는 같음, 1개 파일로만 묶음)
    tar cvf my.tar .
     명령어 묶는다   묶음 파일이름 대상
     -> 실행해도 기존 파일이 사라지지 않는다.
         묶은 파일은 압축되지 않는다.
         tar cvf ../my.tar .
           (상위 디렉토리에 tar 파일을 생성)

    tar xvf my.tar
     명령어 품   풀 대상
     xvfC --> 풀 대상 다음에 디렉토리 지정해서 풀수 있음
    -> tar vxfC my .tar test

 ps. 옵션 정보
     c (묶음)
     x (풀다)
     v (본다)
     f (파일, 필수로 넣어야 되는 옵션 값)

ps2. + 그러면 묶음 + 압축을 같이 하는 방법은?
    옵션에 z 를 붙이면 gzip 으로 압축해줌
    묶음 : tar cvfz my.tar.gz
    풀    : tar xvfz my.tar.gz
            tar xvfzC my.tar.gz test
              (test 디렉토리에 풀겠다.)
 
2-2) 몇개 파일을 설치해보자
    php, apache, mysql

지금까지 linux에서 ip가 안잡혔었는데
  http://egloos.zum.com/redflag/v/433963
  해당 블로그 2번 사항 진행시 해결됨
  동적 ip 할당의 경우 방법이 있었는데
  BOOTPROTO=dhcp → IP동적 할당으로 설정 

  자동으로 ip 할당할 방법이 여기도 있을거라 생각해서
   검색해봤는데 잘 적용됨

--------------------------------------------------------------
먼저 ftp 접속후
/root 밑에 test 폴더를 만들고 거기에 받을 예정

   ftp xxx.xxx.xxx.xxx
내용을 확인하고
   ls  <-- ftp 접속한 폴더에서의 ls 명령
   !ls <-- 리눅스 폴더에서의 ls 명령

binary <--mode를 binary로 해놔야 정상적으로 다운 가능
             ascii 모드로 했다가 압축 다 풀고 설치가
             안되는 경우가 있음

prompt <-- 파일 다운로드시
                매번 물어보는걸 생략을 위해서 off 로 설정

mget * <-- 떠있는 파일을 다 가져온다.

bye  <-- 다 받고 ftp 종료

압축을 이제 풀어야 하는데

tar xvfzC apache-tomcat-6.0.443.tar.gz /apm

du -sh : 현재 디렉토리 용량 확인










2015년 7월 25일 토요일

리눅스 서버 알아보기_03. 사용자 환경설정 파일

/root <-- root 사용자의 홈
/home/계정명 <-- 일반사용자의 홈

w -> 시스템에 접속한 유저 리스트
       who, whoami, who am i 보다
       많은 정보를 보여줌

사용자를 만들면
/etc/passwd 파일에 등록됨
root 계정은 uid-->0
----------------------------
시스템에 사용자는 계정 1~499
----------------------------
일반계정은 uid -->500번부터 부여

grep 주어진 문자열이 포함된 열을 알려준다.
grep babo /etc/passwd
     --> /etc/passwd 파일에 babo가 들어간
          문자열을 뽑아줌
         보통 cat,ls의 광역 검색 등과 같이 쓰인다.
ex)
cat -n etc/passwd|grep babo
    -n옵션은 줄수를 표시함

ps -ef|grep httpd
     프로세서중 httpd 웹데몬이 떠있는지 확인하기
      위한 명령

rpm -qa
     설치된 프로그램 리스트 출력
     rpm -qa|grep telnet
     rpm -qa|grep httpd

chsh -l
     현재 보유중인 쉘 리스트를 보여준다.
      리눅스는 보통 /bin/bash를 사용
      유닉스는 bin/sh <-- 본쉘을 주로쓴다.

chsh
     /bin/sh
     --> 쉘 교체됨

echo $SHELL <-- 현재 적용되어 있는 쉘 확인

env <-- 접속 사용자가 보유한 환경 설정을 보여준다.
           꽤 많은 정보가 보이고 $PATH, $SHELL이
           다 포함되어 있다.

echo $PATH
PATH=$PATH:/root
echo $PATH
          /root 가 PATH에 추가됨 다만 이건 logout 하면
          사라진다.

         .bashrc 를 수정하면 나중에 접속해도
          적용 가능해짐
ls -alsh
 -h 는 파일 크기를 포함해서 보여준다.
-F  는 디렉토리는 / 로 보여줘서 파일과 구분해서 보여줌

.bashrc에 있는 쉘 조건문 확인

if [-f /etc/bashrc ]; then
       . /etc/bashrc
fi

if(조건문)-f (존재한다면) 디렉토리 then(진행한다)
      .(실행할것) /etc/bashrc
if(조건문 끝)
==> etc/밑에 bashrc 파일이 있으면
       etc/bashrc를 실행(읽어들)한다.

. .bashrc <-- .bashrc를 실행할 것.

rpm -qa|grep 프로그램명
rpm -qa|grep 데몬명
. .환경설정파일 <-- 바로 설정한걸 적용 또는
                          읽어들임(적용한다. 다 같은의미)
                         로그아웃 필요없이 변경 적용됨


2015년 7월 24일 금요일

신입SW인력을 위한 실전 자바(Java) 스프링(Spring) 동영상과정 14_@RequestMapping에 대해서

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

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

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

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

1. @RequestMapping 으로 get, post 방법 구별하여 처리하기
   form 변수를 get으로 보내느냐 post로 보내느냐에 따라
   controller단에서 반응하는 method가 틀리게 작성이 가능하다.

index.jsp 에서 form 을 하나 작성하고 아래와 같이 body에 작성한다.

<form action="student" method="get">
student id : <input type="text" name="id"> <br />
<input type="submit" value="전송">
</form>

Controller 에서 다음과 같이 메서드를 작성한다.

//폼 입력 화면으로 요청시 진행
 @RequestMapping("/index")
 public String goIndex() {
  return "index";
 }
//폼에서 입력후 버튼 누르면 student action을 받아 진행
// 이때 RequestMethod 는 get 방식인지 확인한다.(get방식만 받는다)
// 액션값이 같아도 GET 요청만 처리된다.
 @RequestMapping(method = RequestMethod.GET, value = "/student")
 public String goStudent(HttpServletRequest httpServletRequest, Model model) {
  String id = httpServletRequest.getParameter("id");
  model.addAttribute("studentId", id);
  return "student/studentId";
 }


views 밑에 student 폴더를 생성후 그 밑에 students.jsp 파일을
   만들어 내용을 작성한다.

<%@ page language="java" contentType="text/html; charset=EUC-KR"
    pageEncoding="EUC-KR"%>
<!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=EUC-KR">
<title>Insert title here</title>
</head>
<body>

studentId : ${studentId}

</body>
</html>


http://localhost:8181/ex/index
링크로 실행후 id값에 2 를 입력후 버튼을 누르면
   get 방식이기 때문에
http://localhost:8181/ex/student?id=2
   라고 링크가 변하면서 진행된다.

여기서 form 작성 jsp 에서 method="get" 을
   method="post" 로 변경시에는 실행되지 않는다.

controller 에서 RequestMethod.GET 부분을
    RequestMethod.POST 로 바꾸면 실행되고
   결과 링크는 post 방식이기 때문에
http://localhost:8181/ex/student
   라고 링크에 id 값이 포함되지 않고 전달된다.


2. redirect 키워드
   컨트롤러 단에서 return 시 redirect 라는 키워드를 앞에 붙이면
    view 페이지를 가지 않고
    Controller에서 다시 redirect 다음 주어진 키워드를 찾게된다.

@RequestMapping("/studentConfirm")
 public String studentRedirect(HttpServletRequest httpServletRequest, Model model){
  
  String id = httpServletRequest.getParameter("id");
  if(id.equals("abc")) {
   return "redirect:studentOk";
  }
  return "redirect:studentNg";
 }
 
 @RequestMapping("/studentOk")
 public String studentOk(Model model){
  
  return "student/studentOk";
 }
 
 @RequestMapping("/studentNg")
 public String studentNg(Model model){
  return "student/studentNg";
 }


   여기서 id 라는 값을 받아 해당 값이 abc 면 Controller의
     studentOk 값을 다시 찾아 해당 메서드를 실행하게되고
     그 이외에는 studentNg 값을 찾아 해당 메서드를 실행하여
     해당 Controller 대로 진행하게 된다.
   redirect는 또한 jsp 파일도 직접 지정가능한데 WEB-INF 안에 없는
     파일로는 direct 접근이 가능하다 (WEB-INF 밑에는 Controller로만
     접근가능) 이럴때도 redirect 키워드로 접근할 수 있다.

Controller 단에서 return 값을 다음과 같이 준다.
return "redirect:http://localhost:8181/IDE 에서 설정한 프로젝트명/작성한jsp파일.jsp";

신입SW인력을 위한 실전 자바(Java) 스프링(Spring) 동영상과정 12,13,15_컨트롤러 작성_form에서 데이터 받기, 데이터 검증

이 게시물은
http://www.wiz.center/242
http://www.wiz.center/243
http://www.wiz.center/245

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

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

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

1. 컨트롤러 클래스 작성

src 밑에 클래스를 작성 (설정파일에 명시된 패키지 밑에)
   작성된 클래스에 @Controller 를 명시

@Controller
public class MyController {
.......


2. requestMapping 으로 경로 지정 및 리턴

컨트롤러 클래스 밑에 메소드를 하나 작성하는데
   보통(관습적으로) 호출되는 jsp 파일 이름으로 작성한다.
   public String view() 를 작성
   view() return 값은 "view" 로 하고
  WEB-INF/views 밑에 view.jsp 파일을 만든다

.......
@RequestMapping("/view")   // 요청경로
 public String view(){
                .......
  return "view";     // 뷰페이지 이름
}
.......


여기서 호출되는 뷰페이지는 prefix + 뷰페이지이름+suffix 이다.

case2. requestMapping 은 class에도 적용가능하고
   이때는 class Mapping 값 + 메서드 Mapping 값이 된다.

.......
@Controller
@RequestMapping("/view")
public class MyController {
.......

@RequestMapping("/content")   // 요청경로
 public String view(){
.......


=================> /view/content 패키지에서 찾는다.


3. 값을 페이지로 넘기는 방법

.......
@RequestMapping("/view")   // 요청경로
 public String view(Model model){
                model.addAttribute("id", 30);
  return "view";     // 뷰페이지 이름
}
.......


  메서드에 Model 이라는 클래스 를 인자로 받아낼 수 있으며
   model 객체에 함수를 이용하는 방법으로 값을 넣을 수 있음.
   여기서는 id 라는 이름에 30이라는 값을 넣었음.
  model 객체는 따로 리턴하지 않아도 Spring Container 에서
   자동으로 해당 값을 넣어줌

jsp 에 다음 내용을 추가후
   id : ${id}
 서버 재가동 후에 확인하면 됨


+ 함수에 Model 인수를 받지 않고
    함수 안에서 ModelAndView mv 객체를
    생성해서 작업하는 방법도 있음 여기에서는 

.......
 public ModelAndView view(){
     ModelAndView mv = new ModelAndView();
     mv.addObject("id", 30);
     mv.setViewName("view");
  return mv;
 }
.......


    Model 인수를 받지 않는대신에 mv 객체에
     view 장소를 담고 mv 객체를 리턴해야 된다.

4. Form 에서 데이터 받기
 1) HttpServletRequest 클래스

@RequestMapping("board/confirmId")
public String confirmId(HttpServletRequest httpServletRequest, Model model) {
 String id = httpServletRequest.getParameter("id");
 String pw = httpServletRequest.getParameter("pw");
 model.addAttribute("id", id);
 model.addAttribute("pw", pw);
 return "board/confirmId";
}


   Controller 단의 메소드에서 HeepServletRequest 
    인자를 받아오는데 해당 객체에는 사용자가 요청한
    내용이 포함되어 있음.
   views/board/confirmId.jsp 에 파일을 생성후

@<page language="java" contentType="text/html; charset=EUC-KR"
    pageEncoding="EUC-KR">
.......
<body>
 ID : ${id}
 PW : ${pw}
.........
</body>
</pre>

   이렇게 작성후 아래와 같이 실행시
http://localhost:8181/ex/board/confirmId?id=kim&pw=kim2323


입력한 id, pw 가 화면에 보이는걸 확인 가능하다.


 2) @RequestParam 어노테이션
위 컨트롤러 밑의 메서드 내용을 아래처럼 변경 가능하다

@RequestMapping("board/confirmId")
public String confirmId(@RequestParam("id") String id, @RequestParam("pw") String pw , Model model) {
 model.addAttribute("id", id);
 model.addAttribute("pw", pw);
 return "board/confirmId";
}


다만 이때는 RequestParam 에서 지정한 형식을 지켜서 반드시
   인자를 입력해야 정상적으로 실행된다.
   받기로한 인자(id, pw) 가 없거나 일치하지 않는 경우
   400 에러를 보내게된다.

 3) 데이터(커맨드) 객체
@RequestParam 의 경우 받아야할 데이터가 많아지면 길어진다.
   이를 데이터(객체)로 담아서 쓸수 있음

우선 Member 라는 클래스에는 set, get함수를 포함한 id,pw를
   멤버변수로 가지도록 작성한다. (간단해서 생략)
그리고 위에 작성한 예제를 내용을 아래와 같이 변경한다.

public String confirmId(User us){
 return "board/confirmId";
}


jsp 파일에서는 위의 내용을 아래와같이 수정한다.

........
<body>
 ID : ${user.id}

 PW : ${user.pw}
</body>
.......
이렇게만 해두면 member에 포함된 id, pw를 
   Framework 에서 자동으로 addAttribute 까지 처리한다.
   이때 객체이름은 us 라고 표시했지만 jsp단에서는
   클래스 이름 의 맨 앞글자를 소문자로 변경한
   이름으로 받아야지 받아진다.

이부분을 사용자가 객체이름을 바꿔 사용하고자 하는 경우
Controller 단에서

public String confirmId(@ModelAttribute("us") User us){
 return "board/confirmId";
}


라고 @ModelAttribute 로 표시하여 객체이름을
  지정 할 수 있다.

 4) @PathVariable
   요청 패스에 값을 넣어 진행하는 방법이 있는데
     @PathVariabel 를 이용하는 방법이다.
   Controller 의 Method 를 아래와 같이 작성한다.

 @RequestMapping("board/confirmId/{id}")
 public String confirmId(@PathVariable String id, Model model) {
                //String[] member = id.split(",");
  model.addAttribute("id", id);
  //model.addAttribute("pw", pw);
  return "board/confirmId";
 }


   Jsp는 이전 상태로 돌리고
   실행시에 path를 아래와 같이 부여한다.
http://localhost:8181/ex/board/confirmId/kim
   결과적으로 마지막에 부여한 path kim이
   id 라는 항목에 들어간걸 확인할 수 있다.
   여러개를 넣을수는 없고 kim 위치에 , 이용해서
   배열로 넣는 방법이 있다.
위의 주석을 풀어주고 아래 path로 확인시 kim, 1223이라는 키워드가
   각각 id, pw로 보여짐을 볼 수 있다.
http://localhost:8181/ex/board/confirmId/kim,1223

5. Form 데이터 검증
   데이터 받을때 제약조건 등을 물론 client 를 처리 가능하다 그리고
    이를 서버단에서 검증하는 방법을 알아본다.
    (숫자인지, 문자인지, 길이가 유효한지, 빈칸이 있는지 등등)

 1) Validator interface 이용한 검증
     Controller 에서 요청을 처리하는 method 에 와서
     Validator 를 활용하여 유효성 검증후 유효성 검증이 안되면
     해당 화면으로 돌리고 검증되면 view 호출이 진행되는 방식
     이때 인수로 BindingResult 라는 객체를 하나 받고
      validator.validate(인자를 포함한 객체, BindingResult br 객체)를 실행후
      에러 여부를 체크해서 리턴값을 달리 할 수 있다.

Controller 단의 RequestMapping 메서드를 다음과 같이 작성한다.

 @RequestMapping("/student/create")
 public String studentCreate(@ModelAttribute("student") Student student, BindingResult result) {
  
  String page = "createDonePage";
  
  StudentValidator validator = new StudentValidator();
  validator.validate(student, result);
  if(result.hasErrors()) {
   page = "createPage";
  }
  
  return page;
 }


그리고 validator 를 구현한다.
 여기서 errors 객체의 rejuctValue 를 함으로서
  에러를 발생할 수 있다. 그리고 유효성 검증은
  자바 형태로 검증하면 된다.

public class StudentValidator implements Validator {
//검증할 객체의 타입 정보 여기서는 Student.class 에 대해서
//검증한다.
 @Override
        public boolean supports(Class arg0) {
 return Student.class.isAssignableFrom(arg0);
 }
   @Override
public void validate(Object obj, Errors errors) {
 Student student = (Student)obj;

 String studentName = student.getName();

 if(studentName == null || studentName.trim().isEmpty()) {
  System.out.println("studentName is null or empty");
  errors.rejectValue("name", "trouble");
 }
}


이후 jsp 파일 form 에서 진행시
   validate에서 error 여부를 체크하여 Controller 단에서
   다른 페이지로 보낼 수 있는 기준을 제공하게 된다.
    여기에서는 에러를 포함하여 기존 페이지로 보내게 된다.

 2) Validator 를 implements 하여 작성된 ValidationUtils라는 클래스를
     이용하는 방법

Validator 를 위해서 작성한 StudentValidator 클래스에서
기존에 작성했던

 String studentName = student.getName();

 if(studentName == null || studentName.trim().isEmpty()) {
  System.out.println("studentName is null or empty");
  errors.rejectValue("name", "trouble");
 }


내용을 ValidationUtils 클래스를 이용해서 작성시

ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "trouble");


한줄로 표현이 가능하다 if 조건까지 포함된 내용임
  ValidationUtils 클래서에서 이런 형태의
   유효성을 체크할 수 있는 여러 메서드를 제공해준다.


 3) 어노테이션 @Vaild 와 @InitBinder 을 이용하기

Controller에서 Validator 를 호출했던 내용을 아래와 같이 수정한다.
    바뀐점은 sudent 뒤에 @Valid 라는 어노테이션을 붙여준다.

 @RequestMapping("/student/create")
 public String studentCreate(@ModelAttribute("student") @Valid Student student, BindingResult result) {
  
  String page = "createDonePage";
  
//  StudentValidator validator = new StudentValidator();
//  validator.validate(student, result);
  if(result.hasErrors()) {
   page = "createPage";
  }
  
  return page;
 }

 //위의 생략된 부분의 메서드명을 명시해주는 목적으로
 // InitBinder 어노테이션을 추가한다
 @InitBinder
 protected void initBinder(WebDataBinder binder){
  binder.setValidator(new StudentValidator());
 }


pom.xml 에 다음 의존성을 추가해준다.

 
  org.hibernate
  hibernate-validator
  4.2.0.Final
 


이렇게 Validator 객체를 생성하여 호출하는 과정을 진행하지 않았지만
    정상적으로 호출되는걸 확인 할 수 있다.