본문 바로가기
Spring

[Boot] DAO 버전관리_JdbcTemplate & MyBatis

by amoomar 2022. 4. 26.
반응형

 

해당 포스팅에서는 spring Boot를 활용하여 JdbcTemplate와 MyBatis를 각각 적용하여 DAO를 생성해보았다. 목차는 다음과 같다.

1. JdbcTemplate
2. MyBatis
3. 추가사항

 

1. JdbcTemplate

 

1) 프로젝트 생성

spring starter project를 생성한다. 이때 Dependencies할 내용을 확인한다.

프로젝트 생성

 

 

 

 


 

 

2) VO생성

아래와 같은 내용으로 VO를 생성한다.

package com.example.demo;

public class BoardVO {

	// 칼럼은 int이나, String으로 지정해도 DB로 데이터를 보내줄때 문제가 없다.
	private int bid; 
	private String writer;
	private String title;
	private String content;
	
	// getter & setter
	public int getBid() {
		return bid;
	}
	public void setBid(int bid) {
		this.bid = bid;
	}
	public String getWriter() {
		return writer;
	}
	public void setWriter(String writer) {
		this.writer = writer;
	}
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public String getContent() {
		return content;
	}
	public void setContent(String content) {
		this.content = content;
	}
	
	// to String
	@Override
	public String toString() {
		return "BoardVO [bid=" + bid + ", writer=" + writer + ", title=" + title + ", content=" + content + "]";
	}
	
}

 

 


 

 

3) dataSource생성

application.propertis파일에서 prefix, suffix설정과 함께 DataSource와 관련된 설정도 아래의 예시와 같이 추가한다.

# JSP관련 설정 -> prefix의 경로에 suffix라는 형식의 파일이 생성할 것이라는 명령
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp

# DataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/hamdb
spring.datasource.username=root
spring.datasource.password=12341234

 

 

위의 작업을 진행하면서 build.gradle에 설정도 함께 추가한다.

implementation 'org.apache.tomcat.embed:tomcat-embed-jasper'//JSP를 해석할 수 있는 파서
implementation 'javax.servlet:jstl'//JSTL사용을 위한 자원

 

 

기존 프로젝트 생성시 driver를 추가하지 않았기 때문에 오류 발생을 방지하여 라이브러리를 추가해주어야한다. DB에 맞는 라이브러리를 추가해주면 된다.

라이브러리 추가

 

 


 

 

4) interface생성

DAO를 생성하기에 앞서, 기능과 메서드 시그니처에 대해 미리 정의해두고 작업하면 후에 발생할 실수를 대비할 수 있으며 유지보수를 어렵지 않게 할 수 있다.

package com.example.demo;

import java.util.List;

// DAO를 생성할때, 어떤 기능과 메서드 시그니처가 무엇인지를 미리 interface로 지정하면 좋다.
public interface InterBoardDAO {

	List<BoardVO> getAll(BoardVO vo);
	BoardVO getOne(BoardVO vo);
	int insert(BoardVO vo);
	int delete(BoardVO vo);
	
}

 

 


 

 

5) controller작업

처음 페이지에서 모든 board정보를 가지고 메인페이지로 이동할 수 있도록 하기 위해 메서드를 작성한다. 이때 controller는 boardDAO를 멤버변수로 갖는데, 앞으로 DAO가 추가될 것을 고려하여 DAO의 최상위 개념을 멤버변수로 둠으로써 DAO가 추가되더라도 변경사항이 없도록 한다.

package com.example.demo;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class MyController {

	@Autowired
	InterBoardDAO boardDAO; // 앞으로 DAO는 계속 생겨날 것이 예상되니, 최상위 클래스를 멤버변수로 둠
	
	// main()메서드로 이동해서 데이터를 받아오도록 해야한다.
	// 창이 조회되자마자 메인페이지로 이동되도록 한다.
	@RequestMapping("/")
	public String root() {
		return "redirect:main";
	}
	
	@RequestMapping("/main")
	public String main(Model model) {
		model.addAttribute("datas", boardDAO.getAll(null));
		return "main";
	}
	
	@RequestMapping("/board")
	public String board(BoardVO vo, Model model) {
		model.addAttribute("data", boardDAO.getOne(vo));
		return "board";
	}
	
	@RequestMapping("/delete")
	public String delete(BoardVO vo) {
		if(boardDAO.delete(vo)>0) {
			System.out.println("c: 삭제 성공");
		}
		return "redirect:main";
	}
	
	@RequestMapping("/insert")
	public String insert(BoardVO vo) {
		// 등록이 성공하면, 등록된 글의 상세페이지로 이동
		if(boardDAO.insert(vo)>0) {
			List<BoardVO> datas = boardDAO.getAll(null);
			int bid=datas.get(0).getBid();
			return "redirect:board?bid="+bid;
		}
		// 실패했다면, 그냥 메인페이지로 이동
		return "redirect:main";
	}
}

 

 


 

 

6) 파일 생성

prefix의 설정을 지키기 위해서는 main하위에 기타 폴더들이 필요하게 된다.

 

폴더 생성

 

 

 

 


 

 

7) 페이지 작성

main.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>
<html>
<head>
<meta charset="UTF-8">
<title>main</title>
</head>
<body>

   <table border="1">
      <tr>
         <td>글 번호</td>
         <td>제목</td>
         <td>작성자</td>
         <td>삭제</td>
      </tr>
      <c:forEach var="v" items="${datas}">
         <tr>
            <td><a href="board?bid=${v.bid}">${v.bid}</a></td>
            <td>${v.title}</td>
            <td>${v.writer}</td>
            <td><a href="delete?bid=${v.bid}">❌</a></td>
         </tr>
      </c:forEach>
   </table>
   <hr>
   
   <!-- insert.jsp가 views에 있으면 컨트롤러를 거쳐야만 페이지 이동이 가능하므로, 외부로 뺌 -->
   <!-- https://xzio.tistory.com/1345 -->
   <a href="insert.jsp">글 작성하기</a>
   
</body>
</html>

 

글 작성 페이지 경로와 관련된 이미지는 아래에 첨부하였다.

파일 경로

 

 

 

board.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

<h1>상세 페이지</h1>

	<table border="1">
		<tr>
			<td>제목</td>
			<td>${data.title}</td>
		</tr>
		<tr>
			<td>작성자</td>
			<td>${data.writer}</td>
		</tr>
		<tr>
			<td>내용</td>
			<td>${data.content}</td>
		</tr>
	</table>

<a href="main">메인페이지</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;<a href="delete?bid=${data.bid}">글 삭제</a>

</body>
</html>

 

 

insert.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>main</title>
</head>
<body>

<h1>작성 페이지</h1>

<form action="insert" method="post">
	<table border="1">
		<tr>
			<td>제목</td>
			<td><input type="text" name="title"></td>
		</tr>
		<tr>
			<td>작성자</td>
			<td><input type="text" name="writer"></td>
		</tr>
		<tr>
			<td>내용</td>
			<td><input type="text" name="content"></td>
		</tr>
		<tr>
			<td colspan="2" align="right"><input type="submit" value="글 작성"></td>
		</tr>
	</table>
</form>

   <hr>
   <a href="main">메인페이지로 돌아가기</a>

</body>
</html>

 

 


 

 

8) DAO

JdbcTemplate를 활용한 DAO 코드는 다음과 같다.

package com.example.demo;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository("boardDAO")
public class BoardDAO implements InterBoardDAO{

	@Autowired
	private JdbcTemplate jdbcTemplate;

	@Override
	public List<BoardVO> getAll(BoardVO vo) {
		System.out.println("로그: getAll()");
		String sql="select * from board order by bid desc";
		List<BoardVO> datas=jdbcTemplate.query(sql, new BeanPropertyRowMapper<BoardVO>(BoardVO.class)); //RowMappingClass를 생성하지 않아도 된다.
		return datas;
	}

	@Override
	public BoardVO getOne(BoardVO vo) {
		System.out.println("로그: getOne()");
		String sql="select * from board where bid=?";
		vo=jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<BoardVO>(BoardVO.class));
		return vo;
	}

	@Override
	public int insert(BoardVO vo) {
		System.out.println("로그: insert()");
		String sql="insert into board (writer, title, content) values(?, ?, ?)";
		return jdbcTemplate.update(sql, vo.getWriter(), vo.getTitle(), vo.getContent()); // 몇개를 insert했는지 int로 반환
	}

	@Override
	public int delete(BoardVO vo) {
		System.out.println("로그: delete()");
		String sql="delete from board where bid=?";
		return jdbcTemplate.update(sql, vo.getBid());
	}

}

 

 


 

2.  MyBatis

프로젝트 생성과 VO등 언급되지 않은 내용들은 위와 동일하게 작업되었다.

 

1) application.propertis

JSP관련 설정과 DataSource, MyBatis설정 정보를 기입한다.

# JSP관련 설정 -> prefix의 경로에 suffix라는 형식의 파일이 생성할 것이라는 명령
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp

# DataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/hamdb
spring.datasource.username=root
spring.datasource.password=12341234

# MyBatis
# xml파일(SQL)에 대한 위치를 알려주어야한다.
mybatis.mapper-locations=classpath:mybatis/mapper/**/**.xml

 

 


 

2) xml파일 생성

위의 설정에서 MyBatis사용시 어느 위치의 어떤 xml파일과 매핑할것인지를 정해주었기 때문에, mybatis/mapper패키지 내부에 xml파일을 생성해주어야한다. 위치는 아래와 같다.

 

파일위치와 파일명

 

 

 

xml파일에 작성된 코드는 아래와 같다.

package com.example.demo;

import java.util.List;

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

@Repository("boardDAO")
public class BoardDAO implements InterBoardDAO{

	@Autowired
	private SqlSessionTemplate mybatis;

	@Override
	public List<BoardVO> getAll(BoardVO vo) {
		System.out.println("로그: getAll()");
		return mybatis.selectList("BoardDAO.getAll", vo);
	}

	@Override
	public BoardVO getOne(BoardVO vo) {
		System.out.println("로그: getOne()");
		return mybatis.selectOne("BoardDAO.getOne", vo);
	}

	@Override
	public int insert(BoardVO vo) {
		int flag=0;
		System.out.println("로그: insert()");
		if(mybatis.insert("BoardDAO.insert", vo)>0) {
			flag=1;
		}
		return flag;
	}

	@Override
	public int delete(BoardVO vo) {
		int flag=0;
		System.out.println("로그: delete()");
		if(mybatis.delete("BoardDAO.delete", vo)>0) {
			flag=1;
		}
		return flag;
	}

	@Override
	public int update(BoardVO vo) {
		int flag=0;
		System.out.println("로그: update()");
		if(mybatis.insert("BoardDAO.update", vo)>0) {
			flag=1;
		}
		return flag;
	}

}

 

 


 

 

3) build.gradle

gradel파일의 dependency항목 내부에 아래와 같은 자원을 추가한다.

implementation 'org.apache.tomcat.embed:tomcat-embed-jasper'//JSP를 해석할 수 있는 파서
implementation 'javax.servlet:jstl'//JSTL사용을 위한 자원
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.4'//mybatis

 

 


 

4) view 수정 + update메서드 추가

 

main.jsp수정을 통해 insert.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>
<html>
<head>
<meta charset="UTF-8">
<title>main</title>
</head>
<body>

   <table border="1">
      <tr>
         <td>글 번호</td>
         <td>제목</td>
         <td>작성자</td>
         <td>삭제</td>
      </tr>
      <c:forEach var="v" items="${datas}">
         <tr>
            <td><a href="board?bid=${v.bid}">${v.bid}</a></td>
            <td>${v.title}</td>
            <td>${v.writer}</td>
            <td><a href="delete?bid=${v.bid}">❌</a></td>
         </tr>
      </c:forEach>
   </table>
   <hr>
   <a href="insert">글 작성하기</a>

</body>
</html>

이때 jsp파일의 위치는 다음과 같이 수정되었다.

파일 위치

 

controller에서 method의 요청 방식으로 구분하여 작성 페이지 이동 요청인지, 작성을 위한 요청인지를 구분하여 return한다.

	@RequestMapping(value="/insert", method = RequestMethod.GET)
	public String insert_root(BoardVO vo) {
		return "insert";
	}
	
	@RequestMapping(value="/insert", method = RequestMethod.POST)
	public String insert(BoardVO vo) {
		// 등록이 성공하면, 등록된 글의 상세페이지로 이동
		if(boardDAO.insert(vo)>0) {
			List<BoardVO> datas = boardDAO.getAll(null);
			int bid=datas.get(0).getBid();
			return "redirect:board?bid="+bid;
		}
		// 실패했다면, 그냥 메인페이지로 이동
		return "redirect:main";
	}

 

 

board.jsp수정을 통해 별도 페이지를 추가하지 않고, 상세페이지에서 수정이 가능하도록 진행하였다.

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

<h1>상세 페이지</h1>

<form action="update" method="post">
<input type="hidden" name="bid" value="${data.bid}">
	<table border="1">
		<tr>
			<td>제목</td>
			<td><input type="text" name="title" value="${data.title}" required="required"></td>
		</tr>
		<tr>
			<td>작성자</td>
			<td><input type="text" name="writer" value="${data.writer}" readonly="readonly"></td>
		</tr>
		<tr>
			<td>내용</td>
			<td><input type="text" name="content" value="${data.content}" required="required"></td>
		</tr>
		<tr>
			<td colspan="2" align="right"><input type="submit" value="글 변경"></td>
		</tr>
	</table>
<form>

<a href="main">메인페이지</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;<a href="delete?bid=${data.bid}">글 삭제</a>

</body>
</html>

 

 


 

 

5) DAO작업

DAO는 아래와 같이 수정되었다.

package com.example.demo;

import java.util.List;

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

@Repository("boardDAO")
public class BoardDAO implements InterBoardDAO{

	@Autowired
	private SqlSessionTemplate mybatis; // DI될 객체 변경

	@Override
	public List<BoardVO> getAll(BoardVO vo) {
		System.out.println("로그: getAll()");
		return mybatis.selectList("BoardDAO.getAll", vo);
	}

	@Override
	public BoardVO getOne(BoardVO vo) {
		System.out.println("로그: getOne()");
		return mybatis.selectOne("BoardDAO.getOne", vo);
	}

	@Override
	public int insert(BoardVO vo) {
		int flag=0;
		System.out.println("로그: insert()");
		if(mybatis.insert("BoardDAO.insert", vo)>0) {
			flag=1;
		}
		return flag;
	}

	@Override
	public int delete(BoardVO vo) {
		int flag=0;
		System.out.println("로그: delete()");
		if(mybatis.delete("BoardDAO.delete", vo)>0) {
			flag=1;
		}
		return flag;
	}

	@Override
	public int update(BoardVO vo) {
		int flag=0;
		System.out.println("로그: update()");
		if(mybatis.insert("BoardDAO.update", vo)>0) {
			flag=1;
		}
		return flag;
	}

}

 

 


 

 

3. 추가사항

위의 내용에서는 InterDAO와 별개로 SQLsession을 만들어주어 DAO를 따로 생성해주었는데, 부트에서는 Mybatis사용의 경우 어떤 행동을 수행해야하는지 전부 알고 있기 때문에, InterDAO만 있어도 프로그램을 동작할 수 있게 된다.

 

그 역할은 @Mapper이 한다.

package com.example.demo;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;

// DAO를 생성할때, 어떤 기능과 메서드 시그니처가 무엇인지를 미리 interface로 지정하면 좋다.
@Mapper // 이렇게 하면 알아서 DAO랑 결합해줌
public interface InterBoardDAO {

	List<BoardVO> getAll(BoardVO vo);
	BoardVO getOne(BoardVO vo);
	int insert(BoardVO vo);
	int delete(BoardVO vo);
	int update(BoardVO vo);
	
}

 

 

이때 작성된 Namaspace는 위의 내용과 상이하다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.example.demo.InterBoardDAO">

	<select id="getAll" resultType="com.example.demo.BoardVO">
		  SELECT * FROM BOARD ORDER BY BID DESC 
	</select>
	<select id="getOne" resultType="com.example.demo.BoardVO">
		SELECT * FROM BOARD WHERE BID=#{bid}
	</select>
	<insert id="insert">
		INSERT INTO BOARD (WRITER, TITLE, CONTENT) VALUES(#{writer}, #{title}, #{content}) 
	</insert>
	<delete id="delete">
		DELETE FROM BOARD WHERE BID=#{bid}
	</delete>
	<update id="update">
		UPDATE BOARD SET TITLE=#{title},CONTENT=#{content} WHERE BID=#{bid}
	</update>

</mapper>

 

 

 


 

 

반응형