
JdbcTemplate같은 녀석인데 더 줄여버렸다. JDBC vs SQL MAPPER vs ORM]
JDBC, SQL MAPPER, ORM
개발자가 지정한 SQL, 저장 프로시저 그리고 몇 가지 고급 매핑을 지원하는Persistance(영속성, 데이터를 생성한 프로그램이 종료되어도 사라지지 않는 데이터의 특성)프레임워커 이다.

👉 전 프로젝트 복붙하며 늘 하던대로 설정

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'jakarta.servlet.jsp.jstl:jakarta.servlet.jsp.jstl-api:3.0.0'
compileOnly 'jakarta.servlet:jakarta.servlet-api:6.0.0'
implementation 'org.glassfish.web:jakarta.servlet.jsp.jstl:3.0.1'
implementation 'org.apache.tomcat:tomcat-jasper:10.1.16'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-jdbc:3.2.0'
implementation 'mysql:mysql-connector-java:8.0.33'
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.3'
testImplementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter-test:3.0.3'
}
👉 아래 두 줄 추가

프로퍼티에
mybatis.mapper-locations=classpath:mybatis/mapper/**/**.xml
추가
👉 mapper 안의 모든 xml 설정

👉 resources > mybatis > mapper 폴더 생성 후 MyUserDAO.xml 파일 생성
package com.MyBatis;
import lombok.Data;
@Data
public class MyUserDTO {
private String id;
private String name;
}
package com.MyBatis;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface IMyUserDAO {
List<MyUserDTO> list();
}
<?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.MyBatis.IMyUserDAO">
<select id="list" resultType="com.MyBatis.MyUserDTO">
select id, name from myuser
</select>
</mapper>
package com.MyBatis;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class MyController {
@Autowired
private IMyUserDAO userDAO;
@RequestMapping("/")
public @ResponseBody String root(){
return "MyBatis 사용하기";
}
@GetMapping("/user")
public String userListPage(Model model){
model.addAttribute("users", userDAO.list());
return "userList";
}
}
<%@ 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 lang="ko">
<head>
<meta http-equiv="Content-Type" content="text/html"; charset="UTF-8">
<title>Document</title>
</head>
<body>
<%
out.println("MyBatis : Hello World");
%>
<br>
<c:forEach var = "dto" items="${users}">
${dto.id} / ${dto.name} <br>
</c:forEach>
</body>
</html>


👉classpath의 리소스 폴더 안에.xml만MyBatis사용
<mapper namespace="인터페이스"> <select id="메소드명" resultType="결과값 DTO"> [쿼리문]select 속성(id, name...) from myuser ❗이름이 다르면 절대 안된다!! </select> </mapper>
👉 실행
2일차에 만든 Border를 MyBatis로 변경


package com.MyBatisBorder;
public class BorderDTO {
private int id;
private String writer;
private String title;
private String content;
}
package com.MyBatisBorder;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface IBorderDAO {
public List<BorderDTO> listDao();
public BorderDTO viewDao(String id);
public int writeDao(BorderDTO borderDTO);
public int deleteDao(String id);
}
package com.MyBatisBorder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class BorderController {
@Autowired // 자식인 객체
IBorderDAO dao;
@RequestMapping("/")
public String root(){
return "redirect:list";
}
@RequestMapping("/list")
public String userListPage(Model model){
model.addAttribute("list", dao.listDao());
return "list";
}
@RequestMapping("/view")
public String view(@RequestParam("id") String id, Model model){
model.addAttribute("dto", dao.viewDao(id));
return "view";
}
@RequestMapping("/writeForm")
public String writeForm(){
return "writeForm"; // 화면만 나온다
}
@RequestMapping(value = "/write", method = RequestMethod.POST) // POST 방식으로 보낸다
public String write(Model model, BorderDTO bbsDto){ //HttpRequest 필요없이 BBSDto에 담으면 된다
dao.writeDao(bbsDto);
return "redirect:list";
}
@RequestMapping("/delete")
public String delete(@RequestParam("id") String id){
dao.deleteDao(id);
return "redirect:list";
}
}
<?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.MyBatisBorder.IBorderDAO">
<select id="listDao" resultType="com.MyBatisBorder.BorderDTO">
select * from simple_bbs order by id desc
</select>
<select id="viewDao" resultType="com.MyBatisBorder.BorderDTO">
select * from simple_bbs where id = #{param1}
</select>
<insert id="writeDao">
insert into simple_bbs(writer, title, content) values(#{param1}, #{param2}, #{param3})
</insert>
<delete id="deleteDao">
delete from simple_bbs where id = #{param1}
</delete>
</mapper>
<%@ 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 lang="ko">
<head>
<meta http-equiv="Content-Type" content="text/html"; charset="UTF-8">
<title>게시판</title>
</head>
<body>
<table width="500" cellpadding="0" cellspacing="0" border="1">
<tr>
<th>번 호</th>
<th>작성자</th>
<th>제 목</th>
<th>삭 제</th>
</tr>
<tr>
<c:forEach items="${list}" var="dto">
</tr>
<tr>
<td>${dto.id}</td>
<td>${dto.writer}</td>
<td><a href="view?id=${dto.id}">${dto.title}</a></td>
<td><a href="delete?id=${dto.id}">X</a></td>
</tr>
<tr>
</c:forEach>
</tr>
</table>
<p><a href="writeForm">글 작성</a></p>
</body>
</html>
<%@ 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 lang="ko">
<head>
<meta http-equiv="Content-Type" content="text/html"; charset="UTF-8">
<title>Document</title>
</head>
<body>
내용보기 <br>
<hr>
작성자 : ${dto.writer} <br>
제 목 : ${dto.title} <br>
내 용 : ${dto.content} <br>
<hr>
<br><p>
<a href="list">목록보기</a>
</p>
</body>
</html>
<%@ 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 lang="ko">
<head>
<meta http-equiv="Content-Type" content="text/html"; charset="UTF-8">
<title>Document</title>
</head>
<body>
<table width="500" cellpadding="0" cellspacing="0" border="1">
<form action="write" method="post">
<tr>
<td>작성자</td>
<td><input type="text" name="writer" size="100"></td>
</tr>
<tr>
<td>제 목</td>
<td><input type="text" name="title" size="100"></td>
</tr>
<tr>
<td>내 용</td>
<td><input type="text" name="content" size="100"></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="입력">
<a href="list">목록보기</a></td>
</tr>
</form>
</table>
</body>
</html>




위에는 너무 구리다.. 새 프로젝트로 이쁘게 꾸며보고 전에 만들었던 것 처럼 작성시간, 수정 등등 예쁘게 꾸며보자.
create table `mybatisbbs`.`board`(
`num` int not null auto_increment,
`name` varchar(100) not null,
`subject` varchar(100) not null,
`content` text not null,
`registDay` varchar(100),
primary key(num)
)default charset=utf8;
select * from board;
drop table `mybatisbbs`.`board`;


<?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.MybatisTestBoard.IBoardDAO">
<select id="listDao" resultType="com.MybatisTestBoard.BoardDTO">
select * from board order by num desc
</select>
<select id="viewDao" resultType="com.MybatisTestBoard.BoardDTO">
select * from board where num = #{num}
</select>
<insert id="writeDao">
insert into board(name, subject, content, registDay) values(#{name}, #{subject}, #{content}, #{registDay})
</insert>
<update id="updateDao">
update board set name=#{name}, subject=#{subject}, content=#{content}, registDay=#{registDay} where num = #{num}
</update>
<delete id="deleteDao">
delete from board where num = #{num}
</delete>
</mapper>
server.port=8081
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp
#spring.datasource.driver-class-name=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
#spring.datasource.url=jdbc:log4jdbc:mysql://localhost:3306/mybatisbbs
spring.datasource.url=jdbc:mysql://localhost:3306/mybatisbbs
spring.datasource.username=root
spring.datasource.password=1234
mybatis.mapper-locations=classpath:mybatis/mapper/**/**.xml
package com.MybatisTestBoard;
import lombok.Data;
import java.time.LocalDate;
@Data
public class BoardDTO {
private int num; // 글 번호
private String name; // 작성자
private String subject; // 글 제목
private String content; // 글 내용
private LocalDate registDay = LocalDate.now(); // 작성 날짜
}
package com.MybatisTestBoard;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
@Mapper
public interface IBoardDAO {
public List<BoardDTO> listDao();
public BoardDTO viewDao(String num);
public int writeDao(BoardDTO dto);
public int updateDao(BoardDTO dto);
public int deleteDao(String num);
}
package com.MybatisTestBoard;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import java.time.LocalDate;
@Controller
public class BoardController {
@Autowired // 자식인 객체
IBoardDAO dao;
@RequestMapping("/")
public String root(){
return "redirect:list";
}
@RequestMapping("/list")
public String userListPage(Model model){
model.addAttribute("list", dao.listDao());
return "list";
}
@RequestMapping("/view")
public String view(@RequestParam("num") String num, Model model){
model.addAttribute("dto", dao.viewDao(num));
return "view";
}
@RequestMapping("/writeForm")
public String writeForm(){
return "writeForm"; // 화면만 나온다
}
/*@RequestMapping(value = "/write", method = RequestMethod.POST) // POST 방식으로 보낸다
public String write(Model model, BorderDTO bbsDto){ //HttpRequest 필요없이 BBSDto에 담으면 된다
dao.writeDao(bbsDto);
return "redirect:list";
}*/
@RequestMapping(value = "/write", method = RequestMethod.POST)
public String write(BoardDTO dto){
//LocalDate registDay = LocalDate.now();
//model.addAttribute("registDay", registDay);
dao.writeDao(dto);
System.out.println(dto.getRegistDay());
return "redirect:list";
}
@RequestMapping(value = "/update", method = RequestMethod.POST)
public String update(BoardDTO dto){
dao.updateDao(dto);
return "redirect:list";
}
@RequestMapping("/delete")
public String delete(@RequestParam("num") String num){
dao.deleteDao(num);
return "redirect:list";
}
}
<%@ 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 lang="ko">
<head>
<meta http-equiv="Content-Type" content="text/html"; charset="UTF-8">
<title>게시판</title>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-4bw+/aepP/YC94hEpVNVgiZdgIC5+VKNBQNGCHeKRQN+PtmoHDEXuppvnDJzQIu9"
crossorigin="anonymous">
</head>
<body>
<nav class = "navbar navbar-expand navbar-dark bg-dark">
<div class = "container">
<div class = "navbar-header">
<a class = "navbar-brand" href="list">게시판</a>
</div>
</div>
</nav>
<div class="text-bg-info p-3">
<div class="container">
<h1 class="display-1">게시판</h1>
</div>
</div>
<br>
<br>
<div class="container">
<div class="row" align="center">
<div style="padding-top:50px">
<table class="table table-hover">
<tr>
<th>번호</th>
<th>제목</th>
<th>작성시간</th>
<th>글쓴이</th>
</tr>
<c:forEach items="${list}" var="dto">
</tr>
<tr>
<td>${dto.num}</td>
<td><a href="view?num=${dto.num}">${dto.subject}</a></td>
<td>${dto.registDay}</td>
<td>${dto.name}</td>
</tr>
<tr>
</c:forEach>
</tr>
</table>
</div>
<div class="form-group row">
<div class="col-sm-offset-2 col-sm-10">
<p>
<a href="writeForm"
class="btn btn-primary">게시글 작성</a>
</p>
</div>
</div>
</div>
</div>
</body>
</html>
<%@ 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 lang="ko">
<head>
<meta http-equiv="Content-Type" content="text/html"; charset="UTF-8">
<title>Document</title>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-4bw+/aepP/YC94hEpVNVgiZdgIC5+VKNBQNGCHeKRQN+PtmoHDEXuppvnDJzQIu9"
crossorigin="anonymous">
</head>
<body>
<nav class = "navbar navbar-expand navbar-dark bg-dark">
<div class = "container">
<div class = "navbar-header">
<a class = "navbar-brand" href="list">게시판</a>
</div>
</div>
</nav>
<div class="text-bg-info p-3">
<div class="container">
<h1 class="display-1">게시판</h1>
</div>
</div>
<div class="container">
<form action="update?num=${dto.num}" class="form-horizontal" method="post">
<div class="form-group row">
<label class="col-sm-2 control-label">작성자</label>
<div class="col-sm-3">
<input name = "name" class="form-control"
value='${dto.name}'>
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 control-label">글 제목</label>
<div class="col-sm-3">
<input name = "subject" class="form-control"
value='${dto.subject}'>
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 control-label">글 내용</label>
<div class="col-sm-8" style="word-break: break-all">
<textarea rows="10" cols="100" id = "content" name="content" class="form-control">${dto.content}</textarea>
</div>
</div>
<div class="form-group row">
<div class="col-sm-offset-2 col-sm-10">
<p>
<input type="submit" class="btn btn-success" value="수정" >
<!--<a href="update?num=${dto.num}" class="btn btn-success">수정</a>-->
<a href="delete?num=${dto.num}" class="btn btn-warning">삭제</a>
<a href="list"
class="btn btn-primary">목록</a>
</p>
</div>
</div>
</form>
</div>
</body>
</html>
<%@ 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 lang="ko">
<head>
<meta http-equiv="Content-Type" content="text/html"; charset="UTF-8">
<title>게시글 작성</title>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-4bw+/aepP/YC94hEpVNVgiZdgIC5+VKNBQNGCHeKRQN+PtmoHDEXuppvnDJzQIu9"
crossorigin="anonymous">
<script type="text/javascript">
function checkForm() {
if(!document.newWrite.name.value) {
alert("이름을 입력하세요.");
return false;
}
if(!document.newWrite.subject.value) {
alert("제목을 입력하세요.");
return false;
}
if(!document.newWrite.content.value) {
alert("내용을 입력하세요.");
return false;
}
}
</script>
</head>
<body>
<nav class = "navbar navbar-expand navbar-dark bg-dark">
<div class = "container">
<div class = "navbar-header">
<a class = "navbar-brand" href="list">게시판</a>
</div>
</div>
</nav>
<div class="text-bg-warning p-3">
<div class="container">
<h1 class="display-1">게시글 작성</h1>
</div>
</div>
<div class="container">
<form action="write" name="newWrite" class="form-horizontal" method="post" onsubmit="return checkForm()" >
<div class="form-group row">
<label class="col-sm-2 control-label">이름</label>
<div class="col-sm-3">
<input name="name" type="text" class="form-control" placeholder="이름을 입력하세요.">
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 control-label">제목</label>
<div class="col-sm-5">
<input name="subject" type="text" class="form-control" placeholder="제목을 입력하세요.">
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 control-label">내용</label>
<div class="col-sm-8">
<textarea rows="5" cols="50" name="content" class="form-control"
placeholder="내용을 입력하세요."></textarea>
</div>
</div>
<div class="form-group row">
<div class="col-sm-offset-2 col-sm-10">
<input type="submit" class="btn btn-primary" value="등록" >
<input type="reset" class="btn btn-danger" value="다시 입력">
</div>
</div>
</form>
</div>
</body>
</html>




👉 작성



👉 수정



👉 삭제
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%d{yyyy-MM-dd HH:mm:ss}:%-3relative] [%thread]
%-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="com.MyBatisBorder" level="debug"/>
<root level="info">
<appender-ref ref="console"/>
</root>
</configuration>

👉 콘솔창



log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jsSpyLogDelegator
log4jdbc.dump.sql.maxlinelength=0
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%d{yyyy-MM-dd HH:mm:ss}:%-3relative] [%thread]
%-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="com.MyBatisBorder" level="debug"/>
<!-- log4j2-jdbc4 -->
<logger name="jdbc" level="OFF"/>
<logger name="jdbc.sqlonly" level="OFF"/>
<logger name="jdbc.sqltiming" level="DEBUG"/>
<logger name="jdbc.resultset" level="OFF"/>
<logger name="jdbc.resultsettable" level="DEBUG"/>
<logger name="jdbc.connection" level="OFF"/>
<!-- log4j2-jdbc4 -->
<root level="info">
<appender-ref ref="console"/>
</root>
</configuration>
Log4Jdbc Log4j2 JDBC 4 1 » 1.16
implementation 'org.bgee.log4jdbc-log4j2:log4jdbc-log4j2-jdbc4.1:1.16'
server.port=8081
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp
spring.datasource.driver-class-name=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
spring.datasource.url=jdbc:log4jdbc:mysql://localhost:3306/jdbc
spring.datasource.username=root
spring.datasource.password=1234
mybatis.mapper-locations=classpath:mybatis/mapper/**/**.xml


👉 2번 삭제


실행이 올바르게 된다면 적용, 하지만 실행이 중간에서 실패가 되면 이전 실행 흐름 모두 취소
실행성공: 모두 성공실행실패: 실패 지점 이전 모두 실패인터페이스: 다른 기종간에 연결
다음은 다음시간에