
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 |