ㅁ notice-mapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<mapper namespace="noticeMapper">
<resultMap id="noticeResultMap" type="NoticeDto">
<result column="no" property="no" />
<result column="title" property="title" />
<result column="content" property="content" />
</resultMap>
<select id="selectNoticeList" resultMap="noticeResultMap">
select
no
, title
, content
from notice
order
by no desc
</select>
<!-- 번호로 공지사항 조회 -->
<select id="selectNoticeByNo" resultMap="noticeResultMap">
select
no
, title
, content
from notice
where no = #{ noticeNo }
</select>
<!-- 공지사항 등록 -->
<insert id="insertNotice">
insert
into notice
(
no
, title
, content
)
values
(
seq_notice.nextval
, #{ title }
, #{ content }
)
</insert>
<!-- 공지사항 수정 -->
<update id="updateNotice">
update
notice
set title = #{title}
, content = #{content}
where no = #{no}
</update>
</mapper>
- 각각의 기능별로 어떤 쿼리가 필요할지 sql문을 먼저 작성한다.
- 값을 채울 때는 #{ }을 주로 쓴다.
#{ }은 문자열이면 따옴표가 붙은 채로, 숫자면 그대로 값이 들어간다.
${ }는 값을 채울 수 없다.
- 통상 쿼리문들의 id는 dao의 메소드명과 맞춘다.
9시 25분~38분
- dao와 mapper는 1대1이다. 하나의 dao 메소드에 mapper 파일의 하나의 쿼리.
하나의 서비스 메소드에서 여러개의 dao를 호출할 수 있다.
서비스는 기능단위다.
ㅁ NoticeServiceImpl
package com.br.sbatis.service;
import java.util.List;
import org.springframework.stereotype.Service;
import com.br.sbatis.dao.NoticeDao;
import com.br.sbatis.dto.NoticeDto;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Service
public class NoticeServiceImpl implements NoticeService {
private final NoticeDao noticeDao;
@Override
public List<NoticeDto> selectNoticeList() {
return noticeDao.selectNoticeList();
}
@Override
public NoticeDto selectNoticeByNo(int noticeNo) {
return noticeDao.selectNoticeByNo(noticeNo);
}
@Override
public int insertNotice(NoticeDto n) {
return noticeDao.insertNotice(n);
}
@Override
public int updateNotice(NoticeDto n) {
return noticeDao.updateNotice(n);
}
@Override
public int deleteNotice(String[] deleteNo) {
return 0;
}
}
- 순수 마이바티스를 쓸 때느 ㄴ서비스에서 트랜잭션 처리를 했었다. 커밋 등.
스프링은 알아서 커밋처리를 진행해준다. 그래서 우리가 커밋처리를 대신 해줄 필요 없다.
- 하나의 쿼리문이 실행되면 알아서 커밋처리를 해준다. 대신 dml문이 여러개면 트랜잭션 처리를 해야 한다.
- insert는 실패하면 exception이 발생한다. exception이 발생하지 않았다면 성공해서 커밋까지 된거다.
ㅁ NoticeDao
package com.br.sbatis.dao;
import java.util.List;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.stereotype.Repository;
import com.br.sbatis.dto.NoticeDto;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Repository
public class NoticeDao {
private final SqlSessionTemplate sqlSession;
public List<NoticeDto> selectNoticeList() {
return sqlSession.selectList("noticeMapper.selectNoticeList");
}
public NoticeDto selectNoticeByNo(int noticeNo) {
return sqlSession.selectOne("noticeMapper.selectNoticeByNo", noticeNo);
}
public int insertNotice(NoticeDto n) {
return sqlSession.insert("noticeMapper.insertNotice", n);
}
public int updateNotice(NoticeDto n) {
return sqlSession.update("noticeMapper.updateNotice", n);
}
}
ㅁ list.jsp 수정
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h3>공지사항 목록 페이지</h3>
<c:choose>
<c:when test="${ empty list }">
존재하는 공지사항이 없습니다
</c:when>
<c:otherwise>
<table border="1">
<thead>
<tr>
<th>번호</th>
<th>제목</th>
<th>내용</th>
</tr>
</thead>
<tbody>
<c:forEach var="n" items="${ list }">
<tr>
<td>${ n.no }</td>
<td><a href="${contextPath}/notice/detail.do?no=${n.no}">${ n.title }</a></td>
<td>${ n.content }</td>
</tr>
</c:forEach>
</tbody>
</table>
</c:otherwise>
</c:choose>
</body>
</html>
- 제목 부분을 a태그로 링크로 만들었다.
- 쿼리스트링으로 글 번호도 넘긴다. 글 번호로 상세 조회 후 상세 페이지로 이동시킬 예정이다.
ㅁ NoticeController
package com.br.sbatis.controller;
import java.util.List;
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 com.br.sbatis.dto.NoticeDto;
import com.br.sbatis.service.NoticeService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RequestMapping("/notice")
@RequiredArgsConstructor
@Controller
public class NoticeController {
private final NoticeService noticeService;
@GetMapping("/list.do") // /notice/list.do
public void noticeList(Model model) {
List<NoticeDto> list = noticeService.selectNoticeList();
log.debug("list: {}", list);
model.addAttribute("list", list);
// return "notice/list"; void여도 포워딩하려 한다. url mapping("/notice/list")를 가지고 포워딩한다.
}
@GetMapping("/detail.do") // /notice/detail.do
public void noticeDetail(int no, Model model) {
NoticeDto n = noticeService.selectNoticeByNo(no);
model.addAttribute("n",n);
}
}
- url이 /notice/detail.do다. 내가 이동할 페이지의 경로도 /notice/detail이다.
이럴 때는 컨트롤러의 메소드를 void로 둬도 url을 따서 포워딩이 진행된다. . 앞까지만.
- 컨트롤러의 메소드에서 Model 객체가 필요하면 매개변수에 추가만 해주면 자동으로 주입된다.
ㅁ src/main/webapp/WEB-INF/views/notice에 detail.jsp 생성
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h3>공지사항 상세 페이지</h3>
<c:choose>
<c:when test="${ empty n }">
* 조회된 공지사항이 없습니다.
</c:when>
<c:otherwise>
* 조회된 공지사항 정보 <br><br>
번호 : ${ n.no } <br>
제목 : ${ n.title } <br>
내용 : ${ n.content }
</c:otherwise>
</c:choose>
</body>
</html>
===============================================================================
ㅁ 그런데 실행되는 쿼리를 확인하는 방법이 없다. 어떤 값이 채워져서 실행되는지 로그로 출력해서 확인할 수 있는 방법.
ㅁ 실행되는 쿼리를 로그로 출력하기
ㅁ pom.xml
- 쿼리를 로그로 출력하고자 한다면 라이브러리가 하나 더 필요하다.
- <!-- Logging (logback) --> 부분에 붙여넣는다.
- 2번째. 1.16버전을 복붙.
<?xml version="1.0" encoding="UTF-8"?>
<modelVersion>4.0.0</modelVersion>
<groupId>com.br</groupId>
<artifactId>sbatis</artifactId>
<name>04_Spring_MyBatis_AOP</name>
<packaging>war</packaging>
<version>1.0.0-BUILD-SNAPSHOT</version>
<properties>
<java-version>11</java-version>
<org.springframework-version>5.3.27</org.springframework-version>
<org.aspectj-version>1.9.19</org.aspectj-version>
<org.slf4j-version>2.0.7</org.slf4j-version>
</properties>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework-version}</version>
<exclusions>
<!-- Exclude Commons Logging in favor of SLF4j -->
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- AspectJ -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
<!-- Logging (logback) -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.14</version>
<!-- <scope>test</scope> 이건 지워야 함 -->
</dependency>
<!-- (2) slf4j -->
<dependency> <!-- 기존 dependency 중 이것만 남기고 다 지운다.-->
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${org.slf4j-version}</version>
</dependency>
<!-- (3) log4jdbc -->
<dependency>
<groupId>org.bgee.log4jdbc-log4j2</groupId>
<artifactId>log4jdbc-log4j2-jdbc4.1</artifactId>
<version>1.16</version>
</dependency>
<!-- @Inject -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<!-- Servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- Test -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!-- Lombok 라이브러리 추가 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
<!-- Jackson 라이브러리 추가 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.14.2</version>
</dependency>
<!-- db관련 라이브러리 -->
<!-- (1) ojdbc8 -->
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<version>23.2.0.0</version>
</dependency>
<!-- (2) spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- (3) mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.14</version>
</dependency>
<!-- (4) mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<!-- (5) commons-dbcp -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.9.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.9</version>
<configuration>
<additionalProjectnatures>
<projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>
</additionalProjectnatures>
<additionalBuildcommands>
<buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand>
</additionalBuildcommands>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${java-version}</source>
<target>${java-version}</target>
<compilerArgument>-Xlint:all</compilerArgument>
<showWarnings>true</showWarnings>
<showDeprecation>true</showDeprecation>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<configuration>
<mainClass>org.test.int1.Main</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
ㅁ root-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<bean class="org.apache.commons.dbcp2.BasicDataSource" id="dataSource" destroy-method="close">
<!-- <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" /> -->
<property name="driverClassName" value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy" />
<property name="url" value="jdbc:log4jdbc:oracle:thin:@localhost:1521:xe" />
<property name="username" value="sbatis" />
<property name="password" value="sbatis" />
</bean>
<bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory">
<property name="configLocation" value="classpath:config/mybatis-config.xml" />
<property name="dataSource" ref="dataSource" />
</bean>
<bean class="org.mybatis.spring.SqlSessionTemplate" id="sqlSession">
<constructor-arg ref="sqlSessionFactory" />
</bean>
</beans>
- root-context.xml을 수정해야 한다.
- log4jdbc 기능을 이용하려면 driverClassName과 url을 다르게 써야 한다. 공식사이트에 다 나와 있다.
- driverClassName에 log4jdbc에서 제공하는 클래스명을 써야 한다.
"DriverSpy"만 입력하고 ctrl + 스페이스바를 하면 자동완성된다.
- url은 jdbc와 oracle 사이에 log4jdbc를 추가한다.
ㅁ src/main/resources에 "log4jdbc.log4j2.properties" 일반 파일 생성.
- https://log4jdbc.brunorozendo.com/
- log4jdbc.log4j2.properties 파일에 log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator를 홈페이지에서 복사해서 작성한다.
ㅁ logback.xml 수정
- INFO 레벨 이상의 로그를 출력하게끔 작성했다.
- sqlonly나 sqltiming 보면 어떤 쿼리가 완성되어서 실행되는지 볼 수 있다.
- resulttable 보면 조회 결과를 테이블의 형태로 볼 수 있다.
그러나 데이터가 길면 테이블은 어그러진다.
ㅁ logback.xml 수정
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender class="ch.qos.logback.core.ConsoleAppender" name="consoleLog">
<encoder>
<pattern>%-5level: [%date{yyyy-MM-dd HH:mm:ss}] [%logger:%line] - %msg%n</pattern>
</encoder>
</appender>
<logger name="org.springframework" level="INFO" />
<logger name="com.br.sbatis" level="DEBUG" />
<logger name="jdbc.sqlonly" level="off" /> <!-- 쿼리문만 -->
<logger name="jdbc.sqltiming" level="INFO" /> <!-- 쿼리문 + 실행시간 -->
<logger name="jdbc.audit" level="off" /> <!-- JDBC 호출 정보 -->
<logger name="jdbc.resultset" level="off" /> <!-- ResultSet 호출 정보 -->
<logger name="jdbc.resultsettable" level="off" /> <!-- ResultSet 결과 (조회결과 테이블) -->
<logger name="jdbc.conntection" level="off" /> <!-- Connection 호출 정보 -->
<root level="WARN">
<appender-ref ref="consoleLog" />
</root>
</configuration>
- 그런데 한꺼번에 다 키니 너무 많아서 sqltiming만 남기고 다 level 속성에 off를 줘서 끈다.