src/main/resources > mapping > board-mapping.xml 에서 쿼리문을 처리
BoardDAO에서는 board-mapping.xml에서 적은 쿼리문을 언제 사용할지 정의해준다고 생각하면 됨.
board-mapping.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="BoardDAO">
<!-- 글 등록 -->
<insert id="insertBoard">
insert into board (seq,title,writer,content)
values ((select nvl(max(seq),0) + 1 from board), #{title}, #{writer}, #{content})
</insert>
<!-- 글 수정 -->
<update id="updateBoard">
update board set title=#{title}, content=#{content}, where seq=#{seq}
</update>
<!-- 글 삭제 -->
<delete id="deleteBoard">
delete board where seq=#{seq}
</delete>
<!-- 글 상세 조회 -->
<select id="getBoard" resultType="board">
select * from board where seq=#{seq}
</select>
<!-- 글 목록 조회 -->
<select id="getBoardList" resultType="board">
select * from board where title like '%'||#{searchKeyword}||'%'
order by seq desc
</select>
</mapper>
package com.springbook.biz.board;
import com.springbook.biz.board.impl.BoardDAO;
public class BoardServiceClient {
public static void main(String[] args) {
BoardDAO boardDAO = new BoardDAO();
BoardVO vo = new BoardVO();
vo.setTitle("myBatis 제목");
vo.setWriter("홍길동");
vo.setContent("여름아 부탁해");
boardDAO.insertBoard(vo);
}
}
이렇게 하고 실행했는데 이런 경고 메세지가 생겨서 보니
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.apache.ibatis.reflection.Reflector (file:/C:/Users/USER/.m2/repository/org/mybatis/mybatis/3.3.1/mybatis-3.3.1.jar) to method java.lang.Object.finalize()
WARNING: Please consider reporting this to the maintainers of org.apache.ibatis.reflection.Reflector
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
mybatis 버전 문제였다. 3.3.1 버전에서 → 3.5.2 버전으로 업그레이드 한 후 실행했더니 해결
package com.springbook.biz.board;
import java.util.List;
import com.springbook.biz.board.impl.BoardDAO;
public class BoardServiceClient {
public static void main(String[] args) {
BoardDAO boardDAO = new BoardDAO();
BoardVO vo = new BoardVO();
// 글 등록
vo.setTitle("myBatis 제목");
vo.setWriter("홍길동");
vo.setContent("여름아 부탁해");
boardDAO.insertBoard(vo);
vo.setSearchCondition("TITLE");
vo.setSearchKeyword("");
// 상세글 조회
//vo.setSeq(19);
//BoardVO getboard = boardDAO.getBoard(vo);
//System.out.println("상세글 조회 결과: " + getboard);
// 글 수정
//vo.setTitle("오늘의 날씨");
//vo.setContent("맑음");
//vo.setSeq(17);
//boardDAO.updateBoard(vo);
// 글 삭제
//vo.setSeq(29);
//boardDAO.deleteBoard(vo);
// 전체 글 조회
List<BoardVO> boardList = boardDAO.getBoardList(vo);
for (BoardVO board : boardList) {
System.out.println("==>" + board.toString());
}
}
}
조회, 수정, 삭제가 안된다..! 미해결과제
해결 )
추가로 알아 둘 것.
board-mapping.xml
<selectkey keyProperty="seq" resultType="int" order="BEFORE">
select board_seq.nextval as seq from duel
</selectkey>
insert into board (seq,title,writer,content)
values (#{seq}, #{title}, #{writer}, #{content})
<!-- 어떤 값을 따로 쓰고 싶을 때 -->
<!-- order="AFTER" insert 처리가 된 후에 값을 가지고 와라 -->
<selectkey keyProperty="writer" resultType="int" order="AFTER">
select writer from board
</selectkey>
</insert>
<insert id ="hobby">
insert into hobby (writer) values(#{writer}) <!-- 다른 테이블의 writer를 끌어올 수 있다 -->
</insert>
시퀀스 객체를 DB에 만들어 두고 시퀀스 객체를 이용할 수 있다.
board-mapping.xml
<!-- 컬럼이름과 VO객체 멤버 이름 비교, 이름이 다르더라도 자동으로 매치가 된다 -->
<resultMap id="boardResult" type="board">
<id property="seq" column="SEQ"/>
<result property="title" column="TITLE"/>
<result property="writer" column="WRITER"/>
<result property="content" column="CONTENT"/>
<result property="regDate" column="REGDATE"/>
<result property="cnt" column="CNT"/>
</resultMap>
쿼리문을 쓸 때 부등호( <, > ) 인식을 못한다. 그럼으로 부등호를 써야할 때는
<!-- 조건절에 비교연산자(<, >)을 식으로 인식하기 위해 -->
<![CDATA[
insert into board (seq,title,writer,content)
values ((select nvl(max(seq),0) + 1 from board), #{title}, #{writer}, #{content})
]]>
<![CDATA[ ]]> 안에 비교할 대상의 식이 쓰이게 사용해야 한다.
BoardDAO.java
package com.springbook.biz.board.impl;
import java.sql.SQLException;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import com.springbook.biz.board.BoardVO;
import com.springbook.biz.util.SqlSessionFactoryBean;
public class BoardDAO {
// 원형
// public List seleList(String statement)
// public List seleList(String statement, Object parmeter)
//
// public int insert(String statement, Object parmeter)
// public int update(String statement, Object parmeter) throws SQLException
// public int delete(String statement, Object parmeter) throws SQLException
// SqlSession 타입 mybatis은 xml의 정보를 다 가지고 있다.
private SqlSession mybatis;
public BoardDAO() {
mybatis = SqlSessionFactoryBean.getSqlSessionInstance();
}
// 글 등록
public void insertBoard(BoardVO vo) {
mybatis.insert("BoardDAO.insertBoard", vo);
mybatis.commit();
}
// 글 수정
public void updateBoard(BoardVO vo) {
mybatis.update("BoardDAO.updateBoard", vo);
mybatis.commit();
}
// 글 삭제
public void deleteBoard(BoardVO vo) {
mybatis.delete("BoardDAO.deleteBoard", vo);
mybatis.commit();
}
// 글 상세조회
public BoardVO getBoard(BoardVO vo) {
return mybatis.selectOne("BoardDAO.getBoard", vo); // 하나만 조회할때 selectOne
}
// 글 목록 조회
public List<BoardVO> getBoardList(BoardVO vo) {
return mybatis.selectList("BoardDAO.getBoardList", vo); // 여러 개 조회할때 selectList
}
}
Spring에 Mybatis 연결하기
어떤 프로그램을 사용하기 위해서는 접점이 필요하다
Spring에서도 Mybatis을 사용하기 위해서는 SDK → api 인터페이스가 필요.
SDK 을 통해 Mybatis에 접근한다.
자바에서 오라클DB를 쓰는 것과 똑같은 원리. - JDBC가 SDK
다이나믹 SQL = 동적 쿼리문
조건절 분기
BoardWep > board-mapping.xml
<!-- 글 목록 조회 -->
<select id="getBoardList" resultType="board" resultMap="boardResult">
select * from board where title like '%'||#{searchKeyword}||'%'
order by seq desc
</select>
위와 같은 구문 고정이 title인데 동적 처리를 하면
BoardWep > board-mapping.xml
<!-- 글 목록 조회 -->
<select id="getBoardList" resultType="board" resultMap="boardResult">
select * from board
where 1=1
<if test="searchCondition == 'TITLE'">
and title like '%'||#{searchKeyword}||'%'
</if>
<if test="searchCondition == 'CONTENT'">
and content like '%'||#{searchKeyword}||'%'
</if>
order by seq desc
</select>
이렇게 바꿀 수 있다.
where 절의 중복을 막기 위해 where 1=1 를 쓰고 where 절이 들어가는 곳마다 and 를 쓴다.
BoardWep > BoardDAOMybatis
package com.springbook.biz.board.impl;
import java.util.List;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.springbook.biz.board.BoardVO;
@Repository
public class BoardDAOMybatis {
// SqlSessionTemplate이 mybatis 기능을 쓰는 라이브러리
// SqlSessionTemplate 객체를 사용
// 자동의존주입
@Autowired
private SqlSessionTemplate mybatis;
public void insertBoard(BoardVO vo) {
System.out.println("==> Mybatis로 insertBoard() 기능 처리");
mybatis.insert("BoardDAO.insertBoard", vo);
}
public void updateBoard(BoardVO vo) {
System.out.println("==> Mybatis로 updateBoard() 기능 처리");
mybatis.update("BoardDAO.updateBoard", vo);
}
public void deleteBoard(BoardVO vo) {
System.out.println("==> Mybatis로 deleteBoard() 기능 처리");
mybatis.delete("BoardDAO.deleteBoard", vo);
}
public BoardVO getBoard(BoardVO vo) {
System.out.println("==> Mybatis로 getBoard() 기능 처리");
return mybatis.selectOne("BoardDAO.getBoard", vo);
}
public List<BoardVO> getBoardList(BoardVO vo) {
System.out.println("==> Mybatis로 getBoardList() 기능 처리");
return mybatis.selectList("BoardDAO.getBoardList", vo);
}
}
applicationContext
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<!-- 패키지를 스캔해준다. 다 해주는 것은 아니고 표시되어진 클래스만 스캔해준다 -> 이것이 어노테이션 -->
<!-- <context:component-scan base-package="polymorphism"></context:component-scan> -->
<context:component-scan base-package="com.springbook.biz"></context:component-scan>
<!-- database.properties 의 파일을 읽어올 수 있게 해준다 -->
<context:property-placeholder location="classpath:config/database.properties"/>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!--JdbcTemplate 클래스를 통해 DB에 접근 (crud) -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true"/> <!-- get메소드 제외하고 트랜잭션 처리를 해줘라 -->
<tx:method name="*" />
</tx:attributes>
</tx:advice>
<bean id="sqlsession" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpth:sql-map-config.xml"/>
</bean>
<bean class="org.mybatis.spring.SqlSessionTemplate">
<!-- 생성자 의존 주입 -->
<constructor-arg ref="sqlSession"></constructor-arg>
</bean>
<aop:config>
<aop:pointcut expression="execution(* com.springbook.biz..*(..))" id="txPointcut"/> <!-- biz 하위의 모든 메소드를 수정하겠다 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" />
</aop:config>
</beans>
<!-- HttpMessageConverter를 구현한 모든 변환기가 생성된다 -->
<!-- 자바객체를 JSON응답 보디로 변환할 때는 MappingJackson2HttpMessageConverter를 사용한다 -->
<!-- 스프링 컨테이너가 어노테이션 처리를 해준다, 우리가 직접 MappingJackson2HttpMessageConverter을 사용하는 것이 아님.-->
<mvc:annotation-driven></mvc:annotation-driven>
Boardcontroller.java
// 글 목록 JSON으로 변환
@RequestMapping(value = "/dataTransform.do")
// @ResponseBody 어노테이션을 사용하여 자바 객체를 HTTP 응답 본문의 객체로 변환하며 클라이언트로 전송한다
@ResponseBody // 자바형식을 제이슨형식으로 바꿔달라고 요청
public List<BoardVO> dataTransform(BoardVO vo){
vo.setSearchCondition("TITLE");
vo.setSearchKeyword("");
List<BoardVO> boardList = boardService.getBoardList(vo);
return boardList;
}
package com.springbook.biz.board.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import com.springbook.biz.board.BoardVO;
// 비즈니스 계층에서 사용할 수 있도록 빈에 등록
@Repository
public class BoardDAOSpring {
@Autowired
private JdbcTemplate jdbcTemplate;
// 상수형태로 만들어 두었다.
private final String BOARD_INSERT = " insert into board(seq, title, writer, content) values "
+ " ((select nvl(max(seq),0) + 1 from board), ?, ?, ?)";
private final String BOARD_UPDATE = "update board set title=?, content=? where seq=?";
private final String BOARD_DELETE = "delete board where seq=?";
private final String BOARD_GET = "select * from board where seq=?";
private final String BOARD_LIST = "select * from board order by seq desc";
// 검색기능
private final String BOARD_LIST_T = "select * from board where title like '%'||?||'%' order by seq desc";
private final String BOARD_LIST_C = "select * from board where content like '%'||?||'%' order by seq desc";
// ---------- update() 구문 사용 ----------
// 글 등록
public void insertBoard(BoardVO vo) {
System.out.println("===> springJDBC로 insertBoard() 기능 처리");
// insert를 springJDBC에서는 update가 처리, try catch문 사용 안해도 됨.
// 기존 jdbc 에서 사용한 '?' 대신 순서대로 객체를 가지고 와 적어준다.
// jdbcTemplate.update(BOARD_INSERT, vo.getTitle(), vo.getWriter(), vo.getContent());
jdbcTemplate.update(BOARD_INSERT, vo.getTitle(), vo.getWriter(), vo.getContent());
}
// 글 수정
public void updateBoard(BoardVO vo) {
System.out.println("===> springJDBC로 updateBoard() 기능 처리");
jdbcTemplate.update(BOARD_UPDATE, vo.getTitle(), vo.getContent(), vo.getSeq());
}
// 글 삭제
public void deleteBoard(BoardVO vo) {
System.out.println("===> springJDBC로 deleteBoard() 기능 처리");
jdbcTemplate.update(BOARD_DELETE, vo.getSeq());
}
// ---------- queryForObject(), query() 구문 사용 ----------
// 글 상세 조회
public BoardVO getBoard(BoardVO vo) {
System.out.println("===> springJDBC로 getBoard() 상세 보기 처리");
// 배열로 값을 받기 때문에 배열에 넣어준다
Object[] args= {vo.getSeq()};
// return jdbcTemplate.queryForObject(sql, args, rowMapper);
return jdbcTemplate.queryForObject(BOARD_GET, args, new BoardRowMapper());
}
// 글 목록 조회
public List<BoardVO> getBoardList(BoardVO vo) {
System.out.println("==> springJDBC로 getBoardList() 기능 처리");
Object[] args = {vo.getSearchKeyword()};
if (vo.getSearchCondition().equals("TITLE")) {
return jdbcTemplate.query(BOARD_LIST_T, args, new BoardRowMapper());
} else if (vo.getSearchCondition().equals("CONTENT")) {
return jdbcTemplate.query(BOARD_LIST_C, args, new BoardRowMapper());
}
return null;
}
}
package com.springbook.biz.board;
import java.util.List;
public interface BoardService {
// crud
// 글 등록
void insertBoard(BoardVO vo);
// 글 수정
void updateBoard(BoardVO vo);
// 글 삭제
void deleteBoard(BoardVO vo);
// 글 상세조회
BoardVO getBoard(BoardVO vo);
// 글 목록 조회
List<BoardVO> getBoardList(BoardVO vo);
// 페이징 메소드
List<BoardVO> pageList(int page, int pageSize);
PageDTO pagingParam(int page, int pageSize);
}
package com.springbook.biz.board;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
@Mapper
public interface BoardMapper {
@Select("SELECT * FROM board ORDER BY seq DESC LIMIT #{pageStart}, #{pageSize}")
List<BoardVO> pageList(@Param("pageStart") int pageStart, @Param("pageSize") int pageSize);
@Select("SELECT COUNT(*) FROM board")
int boardCount();
// 나머지 메서드들은 기존과 동일
}
정작 BoardDAOSpring은 수정하지 않았는데 접근 자체가 잘못된 것 같다.
다시 해보기 ㅠㅠ
MyBatis
쿼리문을 별도 관리, 분리한다
파일 만들기
여기서 next 를 누르고 조금 기다리면
이런 화면이 나온다
이 파란 부분 선택,
이렇게 설정해 파일을 만든다.
그 후 새로 만든 파일 new → 소스 폴더 누르고 stc/main/java 파일 만들기 → ojdbc6 (맥은 11) 연결하고 톰캣 서버 연결도 해주기.
maven 에서 mybatis, ibatis-core 라이브러리 두 개를 pom.xml에 추가한다.
MyBatis 에서는 BoardDAO에서 쓴 쿼리문을 그대로 사용,
기존 BoardDAO에서 ? 처리된 것을 기존에는 VO를 받아 처리했다면 여기서는 자동으로 VO를 받아와 #{}로 표기한다. 여기서는 VO.getTitle이런 식으로 쓰지 않음.
#{title} 이렇게 표기함으로서 의미를 함축
board-mapping.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="BoardDAO">
<!-- 글 등록 -->
<insert id="insertBoard">
insert into board (seq,title,writer,content)
values ((select nvl(max(seq),0) + 1 from board), #{title}, #{writer}, #{content})
</insert>
<!-- 글 수정 -->
<update id="updateBoard">
update board set title=#{title}, content=#{content}, where seq=#{seq}
</update>
<!-- 글 삭제 -->
<delete id="deleteBoard">
delete board where seq=#{seq}
</delete>
<!-- 글 상세 조회 -->
<select id="getBoard" resultType="board">
select * from board where seq=#{seq}
</select>
<!-- 글 목록 조회 -->
<select id="getBoardList" resultType="board">
select * from board where title like '%'||#{searchKeyword}||'%'
order by seq desc
</select>
</mapper>
태그 속성 자체에 기능의 이름이 붙으며 쿼리문 사용이 보다 직관적이다.
여러 개의 insert 구문을 만들 수 있는데 이를 구분하는 것이 id값
update, delete 등 다른 것도 마찬가지. id값으로 구분하고, <mapper namespace="BoardDAO"> 의 namespace를통해 접근한다.
sql-map-config 에서는 DB 정보를 사용하기 위해 DB 정보가 담겨있는 db.properties 파일 연결해주기.
<properties resource="db.properties" />
sql-map-config 전문
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- Properties 파일 설정 -->
<properties resource="db.properties" />
<!-- Alias 설정 -->
<typeAliases>
<typeAlias alias="board"
type="com.springbook.biz.board.BoardVO" />
</typeAliases>
<!-- DataSource 설정, 트랜잭션 연결 -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${jdbc.dirver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!-- Sql Mapper 설정 -->
<mappers>
<mapper resource="mappings/board-mapping.xml" />
</mappers>
</configuration>
에서 <typeAlias alias="board" type="com.springbook.biz.board.boardVO"/>는 BoardVO를 가리킨다.
type="com.springbook.biz.board.boardVO" 을 지정해줌으로서
BoardVO를 board라고 명명하고
board-mapping.xml에서 BoardVO를 board라고 쓸 수 있게 해준다.
SQL 이랑 연결이 된다는 것이 직접적인 트렌잭션, 쿼리문 처리가 가능하다는 것.
SqlSessionFactoryBean.java
package com.springbook.biz.util;
import java.io.Reader;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class SqlSessionFactoryBean {
// SQL에 연결
// 싱글톤 패턴
// IO 스트림 관련 작업은 다 예외 처리 필요
private static SqlSessionFactory sessionFactory = null;
static {
try {
if (sessionFactory == null) {
Reader reader = Resources.getResourceAsReader("sql-map-config.xml");
sessionFactory = new SqlSessionFactoryBuilder().build(reader);
}
} catch (Exception e) {
e.printStackTrace();
}
}
// SqlSession 정보를 리턴 받으면 sql 정보를 쓸 수 있다.
public static SqlSession getSqlSessionInstance() {
return sessionFactory.openSession();
}
}
BoardDAO.java
package com.springbook.biz.board.impl;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import com.springbook.biz.board.BoardVO;
import com.springbook.biz.util.SqlSessionFactoryBean;
public class BoardDAO {
// SqlSession 타입 mybatis은 xml의 정보를 다 가지고 있다.
private SqlSession mybatis;
public BoardDAO() {
mybatis = SqlSessionFactoryBean.getSqlSessionInstance();
}
// 글 등록
public void insertBoard(BoardVO vo) {
mybatis.insert("BoardDAO.insertBoard", vo);
mybatis.commit();
}
// 글 수정
public void updateBoard(BoardVO vo) {
mybatis.update("BoardDAO.updateBoard", vo);
mybatis.commit();
}
// 글 삭제
public void deleteBoard(BoardVO vo) {
}
// 글 상세조회
public BoardVO getBoard(BoardVO vo) {
return null;
}
// 글 목록 조회
public List<BoardDAO> getBoardList(BoardVO vo) {
return null;
}
}
위 코드를 통해 presentation-layer.xml 에 컨트롤러, 핸들러, 뷰리저버 정보를 저장한다고 정보를 등록한다.
프론트 컨트롤러 DAO 에 직접 접근이 아니라
비즈니스 계층을 통해 액션 컨트롤러로 접근하는 방식으로 수정
스프링컨테이너 안에 보더 서비스, 보더 컨테이너
보더 서비스를 보더 컨테이너 안에 주입.
프론트 컨트롤러의 요청에 의해서 보더컨테이너는 보더 서비스를 오토와이어
BoardController.java
package com.springbook.view.board;
import java.util.HashMap;
import java.util.Map;
import org.aspectj.internal.lang.annotation.ajcDeclareAnnotation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;
import com.springbook.biz.board.BoardService;
import com.springbook.biz.board.BoardVO;
import com.springbook.biz.board.impl.BoardDAO;
@Controller // web.xml에 있는 프론트컨트롤러를 읽어준다
@SessionAttributes("board") // 저장할 때 "board"라는 이름의 객체가 있으면 session 영역에 저장
public class BoardController {
// 통합 컨트롤러
// 변수 선언 - 변수만 선언해서는 아무 정보도 가지고 올 수 없음
// 자동의존주입 방식으로 정보 주입
@Autowired
// private BoardDAO boardDAO;
private BoardService boardService;
// @ModelAttribute 의 특징
// @RequestMapping 메소드가 실행 되기 전에 실행된다.
// @ModelAttribute 메소드의 실행 결과로 리턴되어지는 객체는 자동으로 Model에 저장된다.
// @ModelAttribute 메소드의 실행을 View페이지에서 사용할 수 있다.
@ModelAttribute("conditionMap") // ("") <- View 페이지에서 사용할 이름
public Map<String, String> searchConditionMap() {
Map<String, String> conditionMap = new HashMap<>();
conditionMap.put("제목", "TITLE");
conditionMap.put("내용", "CONTENT");
return conditionMap;
}
// 글 등록
@RequestMapping(value = "/insertBoard.do")
public String insertBoard(BoardVO vo) {
//boardDAO.insertBoard(vo);
boardService.insertBoard(vo);
return "redirect:getBoardList.do";
}
// 글 수정
// @ModelAttribute("board")으로 정보를 가지고 온다
// @SessionAttributes("board")와 실제 같은 값
// 수정한 정보는 vo에 들어있다.
// 한번 들어온 객체들을 넣어두고
// 수정된 title, content는 나중에 처리
// 전체 데이터를 null 데이터 없이 넘겨 처리하기 위해선 이렇게 사용해야 한다.
@RequestMapping(value = "/updateBoard.do")
public String updateBoard(@ModelAttribute("board") BoardVO vo/* , BoardDAO boardDAO */) {
System.out.println(vo.getSeq());
System.out.println(vo.getTitle());
System.out.println(vo.getWriter());
System.out.println(vo.getContent());
System.out.println(vo.getRegDate());
System.out.println(vo.getCnt());
//boardDAO.updateBoard(vo);
boardService.insertBoard(vo);
return "redirect:getBoardList.do";
}
// 글 삭제
@RequestMapping(value = "/deleteBoard.do")
public String deleteBoard(BoardVO vo) {
//boardDAO.deleteBoard(vo);
boardService.insertBoard(vo);
return "redirect:getBoardList.do";
}
// 글 상세조회
@RequestMapping(value = "/getBoard.do")
public String getBoard(BoardVO vo, Model model) {
model.addAttribute("board", boardService.getBoard(vo)); // model은 request 영역에 저장
return "getBoard.jsp";
}
// 글 목록 검색
// 커멘드 객체인 경우
@RequestMapping(value = "/getBoardList.do")
public String getBoardList(BoardVO vo, Model model) {
model.addAttribute("boardList", boardService.getBoardList(vo));
return "getBoardList.jsp";
}
}
이렇게 하고 실행하면 실행되지 않음.
BoardController의 ‘private BoardService boardService;’ 가 생성되지 않았기 때문.
web.xml 에 추가해야 ‘private BoardService boardService;’ 를 읽어올 수 있다.
package com.springbook.biz.board.impl;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Repository;
import com.springbook.biz.board.BoardVO;
import com.springbook.biz.common.JDBCUtil;
@Repository("boardDAO")
public class BoardDAO {
private Connection conn = null;
private PreparedStatement stmt = null;
private ResultSet rs = null;
// SQL 명령어
// 상수형태로 만들어 두었다.
private final String BOARD_INSERT = " insert into board(seq, title, writer, content) values "
+ " ((select nvl(max(seq),0) + 1 from board), ?, ?, ?)";
private final String BOARD_UPDATE = "update board set title=?, content=? where seq=?";
private final String BOARD_DELETE = "delete board where seq=?";
private final String BOARD_GET = "select * from board where seq=?";
private final String BOARD_LIST = "select * from board order by seq desc";
// 검색기능
private final String BOARD_LIST_T = "select * from board where title like '%'||?||'%' order by seq desc";
private final String BOARD_LIST_C = "select * from board where content like '%'||?||'%' order by seq desc";
// 글 등록
public void insertBoard(BoardVO vo) {
System.out.println("===> JDBC로 insertBoard() 기능 처리");
try {
conn = JDBCUtil.getConnection();
stmt = conn.prepareStatement(BOARD_INSERT);
stmt.setString(1, vo.getTitle());
stmt.setString(2, vo.getWriter());
stmt.setString(3, vo.getContent());
stmt.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.close(stmt, conn);
}
}
// 글 수정
public void updateBoard(BoardVO vo) {
System.out.println("===> JDBC로 updateBoard() 기능 처리");
try {
conn = JDBCUtil.getConnection();
stmt = conn.prepareStatement(BOARD_UPDATE);
stmt.setString(1, vo.getTitle());
stmt.setString(2, vo.getContent());
stmt.setInt(3, vo.getSeq());
stmt.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.close(stmt, conn);
}
}
// 글 삭제
public void deleteBoard(BoardVO vo) {
System.out.println("===> JDBC로 deleteBoard() 기능 처리");
try {
conn = JDBCUtil.getConnection();
stmt = conn.prepareStatement(BOARD_DELETE);
stmt.setInt(1, vo.getSeq());
stmt.executeUpdate();
} catch (Exception e){
e.printStackTrace();
} finally {
JDBCUtil.close(stmt, conn);
}
}
// 글 상세 조회
public BoardVO getBoard(BoardVO vo) {
BoardVO board = new BoardVO();
System.out.println("===> JDBC로 getBoard() 상세 보기 처리");
try {
conn = JDBCUtil.getConnection();
stmt = conn.prepareStatement(BOARD_GET);
stmt.setInt(1, vo.getSeq());
rs = stmt.executeQuery();
if (rs.next()) {
board.setSeq(rs.getInt("SEQ"));
board.setTitle(rs.getString("TITLE"));
board.setWriter(rs.getString("WRITER"));
board.setContent(rs.getString("CONTENT"));
board.setRegDate(rs.getDate("REGDATE"));
board.setCnt(rs.getInt("CNT"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.close(rs, stmt, conn);
}
return board;
}
// 글 목록 조회
public List<BoardVO> getBoardList(BoardVO vo) {
System.out.println("==> JDBC로 getBoardList() 기능 처리");
List<BoardVO> boardlist = new ArrayList<>();
try {
conn = JDBCUtil.getConnection();
// 검색 기능
if (vo.getSearchCondition().equals("TITLE")) {
stmt = conn.prepareStatement(BOARD_LIST_T);
} else if (vo.getSearchCondition().equals("CONTENT")) {
stmt = conn.prepareStatement(BOARD_LIST_C);
}
stmt.setString(1, vo.getSearchKeyword());
rs = stmt.executeQuery();
while (rs.next()) {
BoardVO board = new BoardVO();
board.setSeq(rs.getInt("SEQ"));
board.setTitle(rs.getString("TITLE"));
board.setWriter(rs.getString("WRITER"));
board.setContent(rs.getString("CONTENT"));
board.setRegDate(rs.getDate("REGDATE"));
board.setCnt(rs.getInt("CNT"));
boardlist.add(board);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.close(stmt, conn);
}
return boardlist;
}
}
BoardDAOSpring.java - 실질적으로 현재 연결된 DAO
package com.springbook.biz.board.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import com.springbook.biz.board.BoardVO;
// 비즈니스 계층에서 사용할 수 있도록 빈에 등록
@Repository
public class BoardDAOSpring {
@Autowired
private JdbcTemplate jdbcTemplate;
// 상수형태로 만들어 두었다.
private final String BOARD_INSERT = " insert into board(seq, title, writer, content) values "
+ " ((select nvl(max(seq),0) + 1 from board), ?, ?, ?)";
private final String BOARD_UPDATE = "update board set title=?, content=? where seq=?";
private final String BOARD_DELETE = "delete board where seq=?";
private final String BOARD_GET = "select * from board where seq=?";
private final String BOARD_LIST = "select * from board order by seq desc";
// 검색기능
private final String BOARD_LIST_T = "select * from board where title like '%'||?||'%' order by seq desc";
private final String BOARD_LIST_C = "select * from board where content like '%'||?||'%' order by seq desc";
// ---------- update() 구문 사용 ----------
// 글 등록
public void insertBoard(BoardVO vo) {
System.out.println("===> springJDBC로 insertBoard() 기능 처리");
// insert를 springJDBC에서는 update가 처리, try catch문 사용 안해도 됨.
// 기존 jdbc 에서 사용한 '?' 대신 순서대로 객체를 가지고 와 적어준다.
// jdbcTemplate.update(BOARD_INSERT, vo.getTitle(), vo.getWriter(), vo.getContent());
jdbcTemplate.update(BOARD_INSERT, vo.getTitle(), vo.getWriter(), vo.getContent());
}
// 글 수정
public void updateBoard(BoardVO vo) {
System.out.println("===> springJDBC로 updateBoard() 기능 처리");
jdbcTemplate.update(BOARD_UPDATE, vo.getTitle(), vo.getContent(), vo.getSeq());
}
// 글 삭제
public void deleteBoard(BoardVO vo) {
System.out.println("===> springJDBC로 deleteBoard() 기능 처리");
jdbcTemplate.update(BOARD_DELETE, vo.getSeq());
}
// ---------- queryForObject(), query() 구문 사용 ----------
// 글 상세 조회
public BoardVO getBoard(BoardVO vo) {
System.out.println("===> springJDBC로 getBoard() 상세 보기 처리");
// 배열로 값을 받기 때문에 배열에 넣어준다
Object[] args= {vo.getSeq()};
// return jdbcTemplate.queryForObject(sql, args, rowMapper);
return jdbcTemplate.queryForObject(BOARD_GET, args, new BoardRowMapper());
}
// 글 목록 조회
public List<BoardVO> getBoardList(BoardVO vo) {
System.out.println("==> springJDBC로 getBoardList() 기능 처리");
Object[] args = {vo.getSearchKeyword()};
if (vo.getSearchCondition().equals("TITLE")) {
return jdbcTemplate.query(BOARD_LIST_T, args, new BoardRowMapper());
} else if (vo.getSearchCondition().equals("CONTENT")) {
return jdbcTemplate.query(BOARD_LIST_C, args, new BoardRowMapper());
}
return null;
}
}
BoardController.java
package com.springbook.view.board;
import java.util.HashMap;
import java.util.Map;
import org.aspectj.internal.lang.annotation.ajcDeclareAnnotation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;
import com.springbook.biz.board.BoardService;
import com.springbook.biz.board.BoardVO;
import com.springbook.biz.board.impl.BoardDAO;
@Controller // web.xml에 있는 프론트컨트롤러를 읽어준다
@SessionAttributes("board") // 저장할 때 "board"라는 이름의 객체가 있으면 session 영역에 저장
public class BoardController {
// 통합 컨트롤러
// 변수 선언 - 변수만 선언해서는 아무 정보도 가지고 올 수 없음
// 자동의존주입 방식으로 정보 주입
@Autowired
private BoardService boardService;
// @ModelAttribute 의 특징
// @RequestMapping 메소드가 실행 되기 전에 실행된다.
// @ModelAttribute 메소드의 실행 결과로 리턴되어지는 객체는 자동으로 Model에 저장된다.
// @ModelAttribute 메소드의 실행을 View페이지에서 사용할 수 있다.
@ModelAttribute("conditionMap") // ("") <- View 페이지에서 사용할 이름
public Map<String, String> searchConditionMap() {
Map<String, String> conditionMap = new HashMap<>();
conditionMap.put("제목", "TITLE");
conditionMap.put("내용", "CONTENT");
return conditionMap;
}
@RequestMapping(value = "/insertBoard.do")
public String insertBoard(BoardVO vo) {
//boardDAO.insertBoard(vo);
boardService.insertBoard(vo);
return "redirect:getBoardList.do";
}
// 글 수정
// @ModelAttribute("board")으로 정보를 가지고 온다
// @SessionAttributes("board")와 실제 같은 값
// 수정한 정보는 vo에 들어있다.
// 한번 들어온 객체들을 넣어두고
// 수정된 title, content는 나중에 처리
// 전체 데이터를 null 데이터 없이 넘겨 처리하기 위해선 이렇게 사용해야 한다.
@RequestMapping(value = "/updateBoard.do")
public String updateBoard(@ModelAttribute("board") BoardVO vo) {
System.out.println(vo.getSeq());
System.out.println(vo.getTitle());
System.out.println(vo.getWriter());
System.out.println(vo.getContent());
System.out.println(vo.getRegDate());
System.out.println(vo.getCnt());
boardService.insertBoard(vo);
return "redirect:getBoardList.do";
}
// 글 삭제
@RequestMapping(value = "/deleteBoard.do")
public String deleteBoard(BoardVO vo) {
boardService.insertBoard(vo);
return "redirect:getBoardList.do";
}
// 글 상세조회
@RequestMapping(value = "/getBoard.do")
public String getBoard(BoardVO vo, Model model) {
model.addAttribute("board", boardService.getBoard(vo)); // model은 request 영역에 저장
return "getBoard.jsp";
}
// 글 목록 검색
// 커멘드 객체인 경우
@RequestMapping(value = "/getBoardList.do")
public String getBoardList(BoardVO vo, Model model) {
if(vo.getSearchCondition() == null) {
vo.setSearchCondition("TITLE");
}
if(vo.getSearchKeyword() == null) {
vo.setSearchKeyword("");
}
model.addAttribute("boardList", boardService.getBoardList(vo));
return "getBoardList.jsp";
}
}
getBoardList.jsp 에서 searchCondition 스펠링 수정, BoardVO에서도 수정해줬다.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- 스캔태그를 사용해서 스캔처리, 모두 어노테이션으로 해결 -->
<context:component-scan base-package="com.springbook.view"></context:component-scan>
<!-- 파일업로드 빈 설정, 빈 등록 시 id는 스프링이 읽을 수 있는 방식으로 정해져 있다.-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="100000"></property>
</bean>
</beans>
BoardVO.java
getOriginalFilename(), transferTo(), isEmpyt() 메소드를 사용해 파일 업로드
package com.springbook.biz.board;
import java.util.Date;
import org.springframework.web.multipart.MultipartFile;
public class BoardVO {
private int seq;
private String title;
private String writer;
private String content;
private Date regDate;
private int cnt;
private String searchCondition;
private String searchKeyword;
// 파일 변수를 담는 멤버변수
// web.xml의 빈에서 값을 할당해준다
private MultipartFile uploadFile; // getOriginalFilename(), transferTo(), isEmpyt()
public int getSeq() {
return seq;
}
public void setSeq(int seq) {
this.seq = seq;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getWriter() {
return writer;
}
public void setWriter(String writer) {
this.writer = writer;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Date getRegDate() {
return regDate;
}
public void setRegDate(Date regDate) {
this.regDate = regDate;
}
public int getCnt() {
return cnt;
}
public void setCnt(int cnt) {
this.cnt = cnt;
}
public String getSearchCondition() {
return searchCondition;
}
public void setSearchCondition(String searchCondition) {
this.searchCondition = searchCondition;
}
public String getSearchKeyword() {
return searchKeyword;
}
public void setSearchKeyword(String searchKeyword) {
this.searchKeyword = searchKeyword;
}
public MultipartFile getUploadFile() {
return uploadFile;
}
public void setUploadFile(MultipartFile uploadFile) {
this.uploadFile = uploadFile;
}
@Override
public String toString() {
return "BoardVO [seq=" + seq + ", title=" + title + ", writer=" + writer + ", content=" + content + ", regDate="
+ regDate + ", cnt=" + cnt + "]";
}
}
BoardController.java
package com.springbook.view.board;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.aspectj.internal.lang.annotation.ajcDeclareAnnotation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.multipart.MultipartFile;
import com.springbook.biz.board.BoardService;
import com.springbook.biz.board.BoardVO;
import com.springbook.biz.board.impl.BoardDAO;
@Controller // web.xml에 있는 프론트컨트롤러를 읽어준다
@SessionAttributes("board") // 저장할 때 "board"라는 이름의 객체가 있으면 session 영역에 저장
public class BoardController {
// 통합 컨트롤러
// 변수 선언 - 변수만 선언해서는 아무 정보도 가지고 올 수 없음
// 자동의존주입 방식으로 정보 주입
@Autowired
// private BoardDAO boardDAO;
private BoardService boardService;
// @ModelAttribute 의 특징
// @RequestMapping 메소드가 실행 되기 전에 실행된다.
// @ModelAttribute 메소드의 실행 결과로 리턴되어지는 객체는 자동으로 Model에 저장된다.
// @ModelAttribute 메소드의 실행을 View페이지에서 사용할 수 있다.
@ModelAttribute("conditionMap") // ("") <- View 페이지에서 사용할 이름
public Map<String, String> searchConditionMap() {
Map<String, String> conditionMap = new HashMap<>();
conditionMap.put("제목", "TITLE");
conditionMap.put("내용", "CONTENT");
return conditionMap;
}
// 글 등록
@RequestMapping(value = "/insertBoard.do")
public String insertBoard(BoardVO vo) throws IllegalStateException, IOException { // BoardVO vo가 커멘드 객체
//boardDAO.insertBoard(vo);
// 파일 업로드
MultipartFile uploadFile = vo.getUploadFile();
if (!uploadFile.isEmpty()) {
String fileName = uploadFile.getOriginalFilename();
uploadFile.transferTo(new File("c://work//uploads//" + fileName)); // 파일 위치 경로
}
boardService.insertBoard(vo);
return "redirect:getBoardList.do";
}
// 글 수정
// @ModelAttribute("board")으로 정보를 가지고 온다
// @SessionAttributes("board")와 실제 같은 값
// 수정한 정보는 vo에 들어있다.
// 한번 들어온 객체들을 넣어두고
// 수정된 title, content는 나중에 처리
// 전체 데이터를 null 데이터 없이 넘겨 처리하기 위해선 이렇게 사용해야 한다.
@RequestMapping(value = "/updateBoard.do")
public String updateBoard(@ModelAttribute("board") BoardVO vo/* , BoardDAO boardDAO */) {
System.out.println(vo.getSeq());
System.out.println(vo.getTitle());
System.out.println(vo.getWriter());
System.out.println(vo.getContent());
System.out.println(vo.getRegDate());
System.out.println(vo.getCnt());
//boardDAO.updateBoard(vo);
boardService.insertBoard(vo);
return "redirect:getBoardList.do";
}
// 글 삭제
@RequestMapping(value = "/deleteBoard.do")
public String deleteBoard(BoardVO vo/* , BoardDAO boardDAO */) {
//boardDAO.deleteBoard(vo);
boardService.insertBoard(vo);
return "redirect:getBoardList.do";
}
// 글 상세조회
@RequestMapping(value = "/getBoard.do")
public String getBoard(BoardVO vo/* , BoardDAO boardDAO */, Model model) {
model.addAttribute("board", boardService.getBoard(vo)); // model은 request 영역에 저장
return "getBoard.jsp";
}
// 글 목록 검색
// 커멘드 객체인 경우
@RequestMapping(value = "/getBoardList.do")
public String getBoardList(BoardVO vo/* , BoardDAO boardDAO */, Model model) {
if(vo.getSearchCondition() == null) {
vo.setSearchCondition("TITLE");
}
if(vo.getSearchKeyword() == null) {
vo.setSearchKeyword("");
}
model.addAttribute("boardList", boardService.getBoardList(vo));
return "getBoardList.jsp";
}
// 글 목록 검색
// 커멘드 객체가 아닌 경우
/*
* @RequestMapping(value = "/getBoardList.do") public String
* getBoardList(@RequestParam(value = "searchCondtion",defaultValue =
* "TITLE",required = false) String condition,
*
* @RequestParam(value = "searchKeyword",defaultValue = "CONTENT",required =
* false) String keyword, BoardVO vo, BoardDAO boardDAO, Model model) {
*
* System.out.println("검색 조건" + condition); System.out.println("검색 단어" +
* keyword);
*
* model.addAttribute("boardList", boardDAO.getBoardList(vo)); return
* "getBoardList.jsp"; }
*/
}
파일이 업로드 된다. 추가적으로 업로드 된 파일 확인 등은 스스로 추가해볼 것.
이번 주까지 스프링 마무리하고 스프링 부트 나갈 것.
스프링 부트 : 스프링으로 애플리케이션을 만들 때 필요한 설정을 간편하게 처리해주는 별도의 프레임워크
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- 스캔태그를 사용해서 스캔처리, 모두 어노테이션으로 해결 -->
<context:component-scan base-package="com.springbook.view"></context:component-scan>
<!-- HttpMessageConverter를 구현한 모든 변환기가 생성된다 -->
<!-- 자바객체를 JSON응답 보디로 변환할 때는 MappingJackson2HttpMessageConverter를 사용한다 -->
<!-- 스프링 컨테이너가 어노테이션 처리를 해준다, 우리가 직접 MappingJackson2HttpMessageConverter을 사용하는 것이 아님.-->
<mvc:annotation-driven></mvc:annotation-driven>
<!-- 파일업로드 빈 설정, 빈 등록 시 id는 스프링이 읽을 수 있는 방식으로 정해져 있다.-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="100000"></property>
</bean>
</beans>
제이슨 형식으로 글 목록을 변환 받은 것을 보기 위해 index.jsp에 a 태그를 추가했다.
InsertBoardController 만 스프링 컨트롤러를 어노테이션으로 수정했을 때 동작이 잘 되는지 확인해보았다. 값이 잘 들어가는 모습이다.
수정된 InsertBoardController
package com.springbook.view.user;
import java.io.UnsupportedEncodingException;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.springbook.biz.board.BoardVO;
import com.springbook.biz.board.impl.BoardDAO;
@Controller
public class InsertBoardController {
// bean, mapping 객체의 역할을 아래 어노테이션이 해결
@RequestMapping(value = "/insertBoard.do")
public void insertBoard(HttpServletRequest request) {
System.out.println("글등록 처리");
// 필터부분도 사용할 필요가 없다
// try {
// request.setCharacterEncoding("UTF-8");
// } catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
// e.printStackTrace();
// }
String title = request.getParameter("title");
String writer = request.getParameter("writer");
String content = request.getParameter("content");
BoardDAO dao = new BoardDAO();
BoardVO vo = new BoardVO();
vo.setTitle(title);
vo.setWriter(writer);
vo.setContent(content);
dao.insertBoard(vo);
// ---------------------------------------------------
// 어노테이션을 사용하지 않고 스프링 컨트롤러를 사용했을 때 ModelAndView를 사용
// ModelAndView mav = new ModelAndView();
// mav.setViewName("redirect:getBoardList.do");
// return mav;
}
}
스프링 어노테이션을 사용해 더 간편하게 수정된 InsertBoardController
package com.springbook.view.user;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.springbook.biz.board.BoardVO;
import com.springbook.biz.board.impl.BoardDAO;
@Controller
public class InsertBoardController {
// bean, mapping 객체의 역할을 아래 어노테이션이 해결
@RequestMapping(value = "/insertBoard.do")
// public void insertBoard(HttpServletRequest request) {
public String insertBoard(BoardVO vo, BoardDAO dao) { // BoardVO vo -> command 객체 (spring container가 관리)
// spring container는 BoardVO의 setter 메소드에 설정된 이름을 이용해 insertBoard.jsp의 id로 파라매터를 받아 처리해준다
// setter 메소드와 id값 파라메터의 이름이 일치해야 한다는 것을 전제로 한다.
System.out.println("글 등록 처리");
dao.insertBoard(vo);
return "redirect:getBoardList.do";
}
}
return "redirect:getBoardList.do";
// 기존에는 forward한 방식으로 되어 있어 새로고침하면 계속 새 글이 등록됨.
// 때문에 redirect: 를 이용해 새로운 페이지를 요청해 새로고침해도 새 글이 등록 되지 않게 처리
스프링 어노테이션을 사용해 더 간편하게 수정된 GetBoardListController.java
package com.springbook.view.user;
import java.util.List;
//import javax.servlet.http.HttpServletRequest;
//import javax.servlet.http.HttpServletResponse;
//import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.springbook.biz.board.BoardVO;
import com.springbook.biz.board.impl.BoardDAO;
@Controller
public class GetBoardListController {
@RequestMapping(value = "/getBoardList.do")
public ModelAndView getBoardList(BoardVO vo, BoardDAO dao, ModelAndView mav) {
System.out.println("글 목록 검색 처리");
mav.addObject("boardList", dao.getBoardList(vo));
mav.setViewName("getBoardList.jsp");
return mav;
}
}
이렇게 수정 후 insertBoard.jsp 를 실행해 글을 올리면 목록에 잘 등록이 되는 것을 볼 수 있다.
로그아웃은 session에 있는 아이디 정보를 지워야 함으로 HttpSession session 을 사용해야 한다.
invalidate : 무효화 하다
forward 방식과 redirect 방식의 차이
forward 방식은 URL에 변화가 생기지 않는다.
서버 내부적으로 Controller가 View에게 작업을 넘기고 View가 jsp 파일을 렌더링하여 결과 페이지를 클라이언트에게 넘긴다. 이를 클라이언트는 알지 못하고 따라서 URL 요청이 바뀌지 않음.
redirect 방식은 URL에 변화가 생긴다.
새로운 경로로 요청을 보내 새로운 request, response가 생긴다.
DB에 변화를 주는 작업 (회원가입, 게시판 글쓰기 등) redirect
시스템 변화 X (단순 조회, 검색) forward
출처)
통합 컨트롤러
BoardController.java
package com.springbook.view.board;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.springbook.biz.board.BoardVO;
import com.springbook.biz.board.impl.BoardDAO;
@Controller
public class BoardController {
// 통합 컨트롤러
// 글 등록
@RequestMapping(value = "/insertBoard.do")
public String insertBoard(BoardVO vo, BoardDAO boardDAO) {
boardDAO.insertBoard(vo);
return "redirect:getBoardList.do";
}
// 글 수정
@RequestMapping(value = "/updateBoard.do")
public String updateBoard(BoardVO vo, BoardDAO boardDAO) {
boardDAO.updateBoard(vo);
return "redirect:getBoardList.do";
}
// 글 삭제
@RequestMapping(value = "/deleteBoard.do")
public String deleteBoard(BoardVO vo, BoardDAO boardDAO) {
boardDAO.deleteBoard(vo);
return "redirect:getBoardList.do";
}
// 글 상세조회
@RequestMapping(value = "/getBoard.do")
public ModelAndView getBoard(BoardVO vo, BoardDAO boardDAO, ModelAndView mav) {
mav.addObject("board", boardDAO.getBoard(vo)); // Model 정보 저장
mav.setViewName("getBoard.jsp"); // View 정보 저장
return mav;
}
// 글 목록 검색
@RequestMapping(value = "/getBoardList.do")
public ModelAndView getBoardList(BoardVO vo, BoardDAO boardDAO, ModelAndView mav) {
mav.addObject("boardList", boardDAO.getBoardList(vo)); // Model 정보 저장
mav.setViewName("getBoardList.jsp"); // View 정보 저장
return mav;
}
}
기존 컨트롤러 다 주석 처리 후
이 컨트롤러로 동작 시켜 보면 잘 되는 것을 확인할 수 있다.
doget 방식과 dopost 방식
get 방식은 화면을 띄워주고 post 방식은 데이터를 처리
커맨드 객체 (Command Object)
HttpServletRequest를 통해 들어온 요청 값을 setter 메서드를 통해 객체에 정의되어있는 속성에 바인딩이 되는 객체
DTO, VO 클래스, setter 메서드를 가지고 있어야 한다.
= 클라이언트가 전달해주는 피라미터 데이터를 주입 받기 위해 사용
HttpServletRequest 나 RequestParam 을 사용하는 것보다 훨씬 간단하다.
요청을 통해 로그인 페이지 실행
loginController.java
package com.springbook.view.user;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.springbook.biz.user.UserVO;
import com.springbook.biz.user.impl.UserDAO;
@Controller
public class LoginController {
// 표준화
@RequestMapping(value = "/login.do", method = RequestMethod.GET)
// @ModelAttribute("이름") 로 이름을 지정해줄 수도 있다.
public String loginView(@ModelAttribute("userVO") UserVO vo) { // UserVO vo 로 생성된 커맨드 객체는 request 영역에 저장, 객체 저장명 -> userVO
System.out.println("로그인 화면으로 이동...");
vo.setId("test");
vo.setPassword("test1234");
return "login.jsp"; // login.jsp가 실행될 때 setId, setPassword 값을 쓸 수있다
}
@RequestMapping(value = "/login.do", method = RequestMethod.POST)
public String login(UserVO vo, UserDAO userDAO, HttpSession session) {
System.out.println("로그인 인증 처리...");
if (userDAO.getUser(vo) != null) {
// 로그인 유무 체크하기
session.setAttribute("id", userDAO.getUser(vo).getId());
session.setAttribute("userName", userDAO.getUser(vo).getName());
return "getBoardList.do";
}else {
return "login.jsp";
}
}
// public String login(UserVO vo, UserDAO userDAO) {
//
// System.out.println("로그인 처리");
// if(userDAO.getUser(vo) != null) {
// return "redirect:getBoardList.do";
// } else {
// return "login.jsp";
// }
}
package com.springbook.view.board;
import java.util.HashMap;
import java.util.Map;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;
import com.springbook.biz.board.BoardVO;
import com.springbook.biz.board.impl.BoardDAO;
@Controller // web.xml에 있는 프론트컨트롤러를 읽어준다
@SessionAttributes("board") // 저장할 때 "board"라는 이름의 객체가 있으면 session 영역에 저장
public class BoardController {
// 통합 컨트롤러
// @ModelAttribute 의 특징
// @RequestMapping 메소드가 실행 되기 전에 실행된다.
// @ModelAttribute 메소드의 실행 결과로 리턴되어지는 객체는 자동으로 Model에 저장된다.
// @ModelAttribute 메소드의 실행을 View페이지에서 사용할 수 있다.
@ModelAttribute("conditionMap") // ("") <- View 페이지에서 사용할 이름
public Map<String, String> searchConditionMap() {
Map<String, String> conditionMap = new HashMap<>();
conditionMap.put("제목", "TITLE");
conditionMap.put("내용", "CONTENT");
return conditionMap;
}
// 글 등록
@RequestMapping(value = "/insertBoard.do")
public String insertBoard(BoardVO vo, BoardDAO boardDAO) {
boardDAO.insertBoard(vo);
return "redirect:getBoardList.do";
}
// 글 수정
// @ModelAttribute("board")으로 정보를 가지고 온다
// @SessionAttributes("board")와 실제 같은 값
// 수정한 정보는 vo에 들어있다.
// 한번 들어온 객체들을 넣어두고
// 수정된 title, content는 나중에 처리
// 전체 데이터를 null 데이터 없이 넘겨 처리하기 위해선 이렇게 사용해야 한다.
@RequestMapping(value = "/updateBoard.do")
public String updateBoard(@ModelAttribute("board")BoardVO vo, BoardDAO boardDAO) {
System.out.println(vo.getSeq());
System.out.println(vo.getTitle());
System.out.println(vo.getWriter());
System.out.println(vo.getContent());
System.out.println(vo.getRegDate());
System.out.println(vo.getCnt());
boardDAO.updateBoard(vo);
return "redirect:getBoardList.do";
}
// 글 삭제
@RequestMapping(value = "/deleteBoard.do")
public String deleteBoard(BoardVO vo, BoardDAO boardDAO) {
boardDAO.deleteBoard(vo);
return "redirect:getBoardList.do";
}
// 글 상세조회
@RequestMapping(value = "/getBoard.do")
public String getBoard(BoardVO vo, BoardDAO boardDAO, Model model) {
model.addAttribute("board", boardDAO.getBoard(vo)); // model은 request 영역에 저장
return "getBoard.jsp";
}
// 글 목록 검색
// 커멘드 객체인 경우
@RequestMapping(value = "/getBoardList.do")
public String getBoardList(BoardVO vo, BoardDAO boardDAO, Model model) {
model.addAttribute("boardList", boardDAO.getBoardList(vo));
return "getBoardList.jsp";
}
}
<form action="login.do" method="post"> 를 DispatcherServlet에 써 놓은 주소로 바꿔준다.
DispatcherServlet 에는 login.do로 페이지를 이동하면 어느 화면을 보여줄지 판단하기 위해 모델1방식에서 작성했던 login_proc 의 코드를 옮겨 넣어준다.
모델 1방식에서는 프론트컨트롤러 없이 login_proc가 바로 화면 이동을 할 수 있게 해줬다.
→ 글 목록 보기
http://localhost:9999/BoardWeb/getBoardList.jsp 이 주소로 직접 실행하면 안되고
http://localhost:9999/BoardWeb/getBoardList.do 로 직접 접근해야한다.
이렇게 접근해도 주소는 .jsp로 바뀌는데 리다이렉트 방식이기 때문
DispatcherServlet
package com.springbook.view.controller;
import java.io.IOException;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.springbook.biz.board.BoardVO;
import com.springbook.biz.board.impl.BoardDAO;
import com.springbook.biz.user.UserVO;
import com.springbook.biz.user.impl.UserDAO;
// @WebServlet("*.do") 라고 쓰는 매핑정보가 web.xml에 들어 있다.
// 프론트 컨트롤러의 역할
public class DispatcherServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
process(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
process(request, response);
}
private void process(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// request, response를 받아서 필요한 작업 처리
// getRequestURI() 포트번호를 제외하고 뒤의 값을 반환
String uri = request.getRequestURI();
String path = uri.substring(uri.lastIndexOf("/")); // /login.do <- 이렇게 '/' 뒤의 주소를 가지고 온다
System.out.println(path);
// .do 뒤에 뭐가 오냐에 따라 판단
if (path.equals("/login.do")) {
System.out.println("로그인 처리");
// login_proc 의 코드. 모델 2에서는 프론트컨트롤러가 proc의 역할을 수행한다.
String id = request.getParameter("id");
String password = request.getParameter("password");
UserVO vo = new UserVO();
vo.setId(id);
vo.setPassword(password);
UserDAO dao = new UserDAO();
UserVO user = dao.getUser(vo);
if(user != null) {
HttpSession session = request.getSession();
session.setAttribute("id", vo.getId());
response.sendRedirect("getBoardList.do");
}else{
response.sendRedirect("login.jsp");
}
} else if (path.equals("/logout.do")) {
System.out.println("로그아웃 처리");
HttpSession session = request.getSession();
session.invalidate();
response.sendRedirect("login.jsp");
} else if (path.equals("/insertBoard.do")) {
System.out.println("글 등록 처리");
request.setCharacterEncoding("UTF-8");
String title = request.getParameter("title");
String writer = request.getParameter("writer");
String content = request.getParameter("content");
BoardVO vo = new BoardVO();
BoardDAO dao = new BoardDAO();
vo.setTitle(title);
vo.setWriter(writer);
vo.setContent(content);
dao.insertBoard(vo);
response.sendRedirect("getBoardList.do");
} else if (path.equals("/updataBoard.do")) {
System.out.println("글 수정 처리");
request.setCharacterEncoding("UTF-8");
String seq = request.getParameter("seq");
String title = request.getParameter("title");
String content = request.getParameter("content");
BoardVO vo = new BoardVO();
BoardDAO dao = new BoardDAO();
vo.setSeq(Integer.parseInt(seq));
vo.setTitle(title);
vo.setContent(content);
dao.updateBoard(vo);
response.sendRedirect("getBoardList.do");
} else if (path.equals("/deleteBoard.do")) {
System.out.println("글 삭제 처리");
request.setCharacterEncoding("UTF-8");
String seq = request.getParameter("seq");
BoardVO vo = new BoardVO();
BoardDAO dao = new BoardDAO();
vo.setSeq(Integer.parseInt(seq));
dao.deleteBoard(vo);
response.sendRedirect("getBoardList.do");
} else if (path.equals("/getBoard.do")) {
System.out.println("글 상세 조회 처리");
String seq = request.getParameter("seq");
BoardVO vo = new BoardVO();
BoardDAO dao = new BoardDAO();
vo.setSeq(Integer.parseInt(seq));
BoardVO board = dao.getBoard(vo);
// 데이터를 session에 담아 최종적으로 "getBoard.jsp"로 페이지 이동
HttpSession session = request.getSession();
session.setAttribute("board", board);
response.sendRedirect("getBoard.jsp");
} else if (path.equals("/getBoardList.do")) {
System.out.println("글 목록 검색 처리");
BoardVO vo = new BoardVO();
BoardDAO dao = new BoardDAO();
List<BoardVO> boardList = dao.getBoardList(vo);
// 목록을 session에 담아 최종적으로 "getBoardList.jsp"로 페이지 이동
HttpSession session = request.getSession();
session.setAttribute("boardList", boardList);
// 실제 화면에 뿌려줄때는 jsp로 가야한다.
response.sendRedirect("getBoardList.jsp");
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<!-- 패키지를 스캔해준다. 다 해주는 것은 아니고 표시되어진 클래스만 스캔해준다 -> 이것이 어노테이션 -->
<!-- <context:component-scan base-package="polymorphism"></context:component-scan> -->
<context:component-scan base-package="com.springbook.biz"></context:component-scan>
<!-- database.properties 의 파일을 읽어올 수 있게 해준다 -->
<context:property-placeholder location="classpath:config/database.properties"/>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!--JdbcTemplate 클래스를 통해 DB에 접근 (crud) -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
BoardDAOSpring
package com.springbook.biz.board.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import com.springbook.biz.board.BoardVO;
// 비즈니스 계층에서 사용할 수 있도록 빈에 등록
@Repository
public class BoardDAOSpring {
@Autowired
private JdbcTemplate jdbcTemplate;
// 상수형태로 만들어 두었다.
private final String BOARD_INSERT = " insert into board(seq, title, writer, content) values "
+ " ((select nvl(max(seq),0) + 1 from board), ?, ?, ?)";
private final String BOARD_UPDATE = "update board set title=?, content=? where seq=?";
private final String BOARD_DELETE = "delete board where seq=?";
private final String BOARD_GET = "select * from board where seq=?";
private final String BOARD_LIST = "select * from board order by seq desc";
// ---------- update() 구문 사용 ----------
// 글 등록
public void insertBoard(BoardVO vo) {
System.out.println("===> springJDBC로 insertBoard() 기능 처리");
// insert를 springJDBC에서는 update가 처리, try catch문 사용 안해도 됨.
// 기존 jdbc 에서 사용한 '?' 대신 순서대로 객체를 가지고 와 적어준다.
jdbcTemplate.update(BOARD_INSERT, vo.getTitle(), vo.getWriter(), vo.getContent());
}
// 글 수정
public void updateBoard(BoardVO vo) {
System.out.println("===> springJDBC로 updateBoard() 기능 처리");
jdbcTemplate.update(BOARD_UPDATE, vo.getTitle(), vo.getContent(), vo.getSeq());
}
// 글 삭제
public void deleteBoard(BoardVO vo) {
System.out.println("===> springJDBC로 deleteBoard() 기능 처리");
jdbcTemplate.update(BOARD_DELETE, vo.getSeq());
}
// ---------- queryForObject(), query() 구문 사용 ----------
// 글 상세 조회
public BoardVO getBoard(BoardVO vo) {
System.out.println("===> springJDBC로 getBoard() 상세 보기 처리");
// 배열로 값을 받기 때문에 배열에 넣어준다
Object[] args= {vo.getSeq()};
// return jdbcTemplate.queryForObject(sql, args, rowMapper);
return jdbcTemplate.queryForObject(BOARD_GET, args, new BoardRowMapper());
}
// 글 목록 조회
public List<BoardVO> getBoardList(BoardVO vo) {
System.out.println("==> springJDBC로 getBoardList() 기능 처리");
// '?' 가 없다. 글 목록 전체 조회이기 때문에 모든 글들을 다 불러오면 된다.
return jdbcTemplate.query(BOARD_LIST, new BoardRowMapper());
}
}
BoardRowMapper
package com.springbook.biz.board.impl;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.RowMapper;
import com.springbook.biz.board.BoardVO;
public class BoardRowMapper implements RowMapper<BoardVO> {
// BoardDAO 의 resultSet의 역할을 수행한다.
@Override
public BoardVO mapRow(ResultSet rs, int rowNum) throws SQLException {
BoardVO board = new BoardVO();
board.setSeq(rs.getInt("SEQ"));
board.setTitle(rs.getString("TITLE"));
board.setWriter(rs.getString("WRITER"));
board.setContent(rs.getString("CONTENT"));
board.setRegDate(rs.getDate("REGDATE"));
board.setCnt(rs.getInt("CNT"));
return board;
}
}