본문 바로가기
Spring

[Spring] MyBatis(4) 공지사항 목록페이지 -> 상세페이지

by moca7 2024. 10. 22.

 

 

 

 

ㅁ 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"%>
 
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>    
<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"%>
   
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>    
<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를 줘서 끈다.