MyBatis
MyBatis는 흔히 SQL 매핑 프레임워크로 분류된다. JDBC 코드의 복잡하고 지루한 작업을 단축시킬 수 있다.
장점
- 자동으로 Connection close() 기능
- MyBatis 내부적으로 PreparedStatement 처리
- #{prop}와 같이 속성을 지정하면 내부적으로 자동 처리
- 리턴 타입을 지정한느 경우 자동으로 객체 생성 및 ResultSet 처리
- 기존의 SQL을 그대로 활용할 수 있음
출처: https://techhan.github.io/study/spring-07/
이전에 jsp로 프로젝트를 만들었을 때나 MyBatis를 배우기 전 스프링을 사용했을 때는 컨트롤러 파일을 기능마다 계속 만들어주어야 해서 복잡하고 보기도 불편했다.
이러한 불편함을 해소하고 코드를 간단하게 만들어주는 MyBatis를 사용하여 다시 게시판을 만들어보기로 한다.
개인 공부용이기 때문에 틀린 부분이 있을 수 있습니다!
오류가 있다면 편하게 지적 부탁드립니다^_^~
먼저 mybatis 패키지를 만들고, 그 안에 db.properties 파일을 생성한다.
driver=oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@localhost:1521:xe
username=mincho
password=mincho
드라이버, url, 오라클 이름과 패스워드를 기입한다.
다음으로 mybatis패키지에 Configuration.xml파일을 생성한다.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="mybatis/db.properties" />
<typeAliases>
<typeAlias type="board.dto.BoardDTO" alias="boardDTO"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mybatis/boardMapper.xml"/>
</mappers>
</configuration>
MyBatis 설정에 필요한 정보들을 입력한다.
코드에 작성한 대로 board.dto패키지에 BoardDTO파일을 작성하고, mybatis패키지에 boardMapper.xml을 생성한다.
boardMapper.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="mybatis.boardMapper">
<select id="listBoard" resultType="boardDTO">
select * from springboard order by num desc
</select>
<insert id="insertBoard" parameterType="boardDTO">
insert into springboard values(springboard_seq.nextval, #{writer},#{email},#{subject},#{passwd},sysdate,0,#{content},#{ip})
</insert>
<select id="getBoard" parameterType="int" resultType="boardDTO">
select * from springboard where num = #{num}
</select>
<update id="plusReadcount" parameterType="int">
update springboard set readcount=readcount+1 where num = #{num}
</update>
<delete id="deleteBoard" parameterType="int">
delete from springboard where num = #{num}
</delete>
<update id="updateBoard" parameterType="boardDTO">
update springboard set writer=#{writer}, email=#{email}, subject=#{subject}, content=#{content} where num=#{num}
</update>
</mapper>
리스트/삽입/글내용보기/조회수/삭제/수정 기능을 mapper에 작성했다.
<select> 태그의 id 속성 값은 메서드의 이름과 동일하게 작성해야 한다. <select> 태그의 경우 resultType 속성은 인터페이스에 선언된 메서드의 리턴 타입과 동일하게 작성한다.
sql문에서 ? 로 표시되는 부분은 ? 대신 #{} 안에 넣어 표기한다.
BoardMapper.java
package mybatis;
import java.io.IOException;
import java.io.Reader;
import java.util.List;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import board.dto.BoardDTO;
public class BoardMapper {
private static SqlSessionFactory sqlMapper;
static {
try {
String resource = "Configuration.xml";
Reader reader = Resources.getResourceAsReader(resource);
sqlMapper = new SqlSessionFactoryBuilder().build(reader);
}catch(IOException e) {
throw new RuntimeException("DB 연결 오류 발생!!" + e.getMessage());
}
}
public static List<BoardDTO> listBoard(){
SqlSession sqlSession = sqlMapper.openSession();
try {
List<BoardDTO> list = sqlSession.selectList("listBoard");
return list;
}finally {
sqlSession.close();
}
}
public static int insertBoard(BoardDTO dto) {
SqlSession sqlSession = sqlMapper.openSession();
try {
int res = sqlSession.insert("insertBoard", dto);
sqlSession.commit();
return res;
}finally {
sqlSession.close();
}
}
public static BoardDTO getBoard(int num, String mode){
SqlSession sqlSession = sqlMapper.openSession();
try {
if (mode.equals("content")) {
sqlSession.update("plusReadcount", num);
sqlSession.commit();
}
BoardDTO dto = sqlSession.selectOne("getBoard", num);
return dto;
}finally {
sqlSession.close();
}
}
public static int deleteBoard(int num, String passwd) {
BoardDTO dto = getBoard(num, "password");
if (!dto.getPasswd().equals(passwd)) {
return -1;
}
SqlSession sqlSession = sqlMapper.openSession();
try {
int res = sqlSession.delete("deleteBoard", num);
sqlSession.commit();
return res;
}finally {
sqlSession.close();
}
}
public static int updateBoard(BoardDTO dto) {
BoardDTO dto2 = getBoard(dto.getNum(), "password");
if (!dto.getPasswd().equals(dto2.getPasswd())) {
return -1;
}
SqlSession sqlSession = sqlMapper.openSession();
try {
int res = sqlSession.update("updateBoard", dto);
sqlSession.commit();
return res;
}finally {
sqlSession.close();
}
}
}
mybatis패키지에 BoardMapper.java를 생성하고, 각 기능별 메소드를 작성한다.
return전에 commit을 써야 수정사항이 제대로 반영된다.
BoardDAOImpl.java
package board.dao;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import board.dto.BoardDTO;
public class BoardDAOImpl implements BoardDAO{
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate =jdbcTemplate;
}
class MyRowMapper implements RowMapper<BoardDTO>{
@Override
public BoardDTO mapRow(ResultSet rs, int arg1) throws SQLException {
BoardDTO dto = new BoardDTO();
dto.setNum(rs.getInt("num"));
dto.setWriter(rs.getString("writer"));
dto.setEmail(rs.getString("email"));
dto.setSubject(rs.getString("subject"));
dto.setPasswd(rs.getString("passwd"));
dto.setReg_date(rs.getString("reg_date"));
dto.setReadcount(rs.getInt("readcount"));
dto.setContent(rs.getString("content"));
dto.setIp(rs.getString("ip"));
return dto;
}
}
@Override
public List<BoardDTO> listBoard() {
String sql = "select * from springboard";
MyRowMapper mapper = new MyRowMapper();
List<BoardDTO> list = jdbcTemplate.query(sql, mapper);
return list;
}
@Override
public BoardDTO getBoard(int num, String mode) {
if (mode.equals("content")) {
jdbcTemplate.update("update springboard set readcount=readcount+1 where num=?", num);
}
String sql = "select * from springboard where num = ?";
MyRowMapper mapper = new MyRowMapper();
BoardDTO dto = jdbcTemplate.queryForObject(sql, mapper, num);
return dto;
}
@Override
public int insertBoard(BoardDTO dto) {
String sql = "insert into springboard values(springboard_seq.nextval, ?,?,?,?,sysdate,0,?,?)";
Object[] values = new Object[] {dto.getWriter(), dto.getEmail(), dto.getSubject(), dto.getPasswd(), dto.getContent(), dto.getIp()};
int res = jdbcTemplate.update(sql, values);
return res;
}
private boolean isPassword(int num, String passwd) {
BoardDTO dto = getBoard(num, "password");
if (dto.getPasswd().equals(passwd)) return true;
return false;
}
@Override
public int deleteBoard(int num, String passwd) {
if (isPassword(num, passwd)) {
return jdbcTemplate.update("delete from springboard where num=?", num);
}else {
return -1;
}
}
@Override
public int updateBoard(BoardDTO dto) {
if (isPassword(dto.getNum(), dto.getPasswd())) {
String sql = "update springboard set writer=?, email=?, subject=?, content=? where num = ?";
Object values[] = new Object[] {dto.getWriter(), dto.getEmail(), dto.getSubject(), dto.getContent(), dto.getNum()};
return jdbcTemplate.update(sql, values);
}else {
return -1;
}
}
}
BoardDAOImpl은 BoardDAO인터페이스를 상속받아서 작성했다.
기존에 connection을 사용했을 때보다 코드가 많이 줄어들었다.
springboard-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns: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-3.1.xsd">
<context:annotation-config />
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="WEB-INF/" />
<property name="suffix" value=".jsp" />
</bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:@localhost:1521:xe"/>
<property name="username" value="mincho"/>
<property name="password" value="mincho"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="boardDAO" class="board.dao.BoardDAOImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
bean에 db설정 및 jdbcTemplate, DAO를 등록한다.
<context:annotation-config /> 는
namespaces에서 context를 체크해야 설정할 수 있다.
BoardController.java
package board.controller;
import java.util.*;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import board.dao.BoardDAO;
import board.dto.BoardDTO;
import mybatis.BoardMapper;
@Controller
public class BoardController {
@RequestMapping("/list_board.do")
public String listBoard(HttpServletRequest req) {
//List<BoardDTO> list = boardDAO.listBoard();
List<BoardDTO> list = BoardMapper.listBoard();
req.setAttribute("listBoard", list);
return "board/list";
}
@RequestMapping(value="write_board.do", method=RequestMethod.GET)
public String writeFormBoard() {
return "board/writeForm";
}
@RequestMapping(value="write_board.do", method=RequestMethod.POST)
public String writeProBoard(HttpServletRequest req, BoardDTO dto) {
dto.setIp(req.getRemoteAddr());
//int res = boardDAO.insertBoard(dto);
int res = BoardMapper.insertBoard(dto);
if (res>0) {
req.setAttribute("msg", "게시글 등록 성공!! 게시글 목록페이지로 이동합니다.");
req.setAttribute("url", "list_board.do");
}else {
req.setAttribute("msg", "게시글 등록 실패!! 게시글 등록페이지로 이동합니다.");
req.setAttribute("url", "write_board.do");
}
return "forward:message.jsp";
}
@RequestMapping("/content_board.do")
public String contentBoard(HttpServletRequest req, @RequestParam int num) {
//BoardDTO dto = boardDAO.getBoard(num, "content");
BoardDTO dto = BoardMapper.getBoard(num, "content");
req.setAttribute("getBoard", dto);
return "board/content";
}
@RequestMapping(value="update_board.do", method=RequestMethod.GET)
public String updateFormBoard(HttpServletRequest req, @RequestParam int num) {
//BoardDTO dto = boardDAO.getBoard(num, "update");
BoardDTO dto = BoardMapper.getBoard(num, "update");
req.setAttribute("getBoard", dto);
return "board/updateForm";
}
@RequestMapping(value="update_board.do", method=RequestMethod.POST)
public String updateProBoard(HttpServletRequest req, BoardDTO dto) {
int res = BoardMapper.updateBoard(dto);
//int res = boardDAO.updateBoard(dto);
if (res > 0) {
req.setAttribute("msg", "게시글 수정 성공!! 게시글 목록페이지로 이동합니다.");
req.setAttribute("url", "list_board.do");
}else if (res < 0) {
req.setAttribute("msg", "비밀번호가 틀렸습니다. 다시 입력해 주세요.");
req.setAttribute("url", "update_board.do?num=" + dto.getNum());
}else {
req.setAttribute("msg", "게시글 수정 실패!! 게시글 보기페이지로 이동합니다.");
req.setAttribute("url", "content_board.do?num=" + dto.getNum());
}
return "forward:message.jsp";
}
@RequestMapping(value="delete_board.do", method=RequestMethod.GET)
public String deleteFormBoard() {
return "board/deleteForm";
}
@RequestMapping(value="delete_board.do", method=RequestMethod.POST)
public String deleteProBoard(HttpServletRequest req,
@RequestParam Map<String, String> params) {
String num = params.get("num");
String passwd = params.get("passwd");
//int res = boardDAO.deleteBoard(Integer.parseInt(num), passwd);
int res = BoardMapper.deleteBoard(Integer.parseInt(num), passwd);
if (res > 0) {
req.setAttribute("msg", "게시글 삭제 성공!! 게시글 목록페이지로 이동합니다.");
req.setAttribute("url", "list_board.do");
}else if (res < 0) {
req.setAttribute("msg", "비밀번호가 틀렸습니다. 다시 입력해 주세요.");
req.setAttribute("url", "delete_board.do?num=" + num);
}else {
req.setAttribute("msg", "게시글 삭제 실패!! 게시글 보기페이지로 이동합니다.");
req.setAttribute("url", "content_board.do?num=" + num);
}
return "forward:message.jsp";
}
}
이전에는 기능별로 파일을 모두 분리하여 작업했지만 이제는 코드 양이 줄어들어 한 파일에 작성한다.
기존에 DAO에 연결했던 부분들을 모두 주석처리하고 BoardMapper에 있는 메소드를 호출했다.
어노테이션과 파라미터 타입
@Controller
컨트롤러 클래스에 @Controller 어노테이션을 작성한다. 해당 어노테이션이 적용된 클래스는 "Controller"임을 나타나고, bean으로 등록되며 해당 클래스가 Controller로 사용됨을 Spring Framework에 알린다.
@RequestMapping
해당 어노테이션이 선언된 클래스의 모든 메소드가 하나의 요청에 대한 처리를 할경우 사용한다. 예를들어, "/student" 요청에 대해 공통적으로 처리해야될 클래스라는 것을 의미한다. 또한, 요청 url에 대해 해당 메소드에서 처리해야 되는 경우에도 사용된다. 이러한 @RequestMapping에 대한 모든 매핑 정보는 Spring에서 제공하는 HandlerMapping Class가 가지고 있다.
@RequestParam
HTTP GET 방식으로 전달되는 URL 의 parameter 값을 가져올때 사용되며, 변수 앞에 작성해주면 된다.
출처: https://develop-log-sj.tistory.com/29
뷰 부분은 jsp때와 달라진 것이 없어서 생략..!
'Study > spring' 카테고리의 다른 글
[JPA] Error: 1364: Field 'id' doesn't have a default value 오류 해결 (0) | 2023.06.20 |
---|---|
스프링 핵심 원리 강의 정리 1 - 객체지향 (0) | 2022.12.23 |
[SpringBoot] 스프링부트 프로젝트 만들기 (0) | 2022.11.29 |
Spring 에러 - org.springframework.jdbc.datasource.DriverManagerDataSource (0) | 2022.09.25 |
스프링 프로젝트 - 게시판 만들기 1(SQLplus DB생성, lombok설치) (0) | 2022.08.12 |