본문 바로가기
네이버클라우드/JAVA 웹 프로그래밍

JAVA 71일차 (2023-09-01) 자바 프로그래밍_69. Mybatis + Spring IoC 컨테이너 = DAO 구현체 자동 생성_개인프로젝트 - 마트 관리 시스템

by prometedor 2023. 9. 1.
## 69. Mybatis + Spring IoC 컨테이너 = DAO 구현체 자동 생성

- Mybatis의 Spring 연동 플러그인을 사용하여 DAO를 자동 생성하는 방법
- Mybatis의 Log4j 활성화 하기

 

MyBatis

https://blog.mybatis.org/

 

The MyBatis Blog

A blog about the the MyBatis data mapper framework.

blog.mybatis.org

 

ㄴ MyBatis for Java > Github Project 선택

=>

ㄴ Repositories 선택

=>

ㄴ spring 선택

=>

ㄴ Essentials > See the published docs 선택

=>

ㄴ Getting Started 선택하여 문서 확인

 

 

 

ㄴ central.sonatype.com 에서 mybatis-spring 검색

=>

ㄴ mybatis-spring 3.0.2 버전 Gradle(short) 버전 코드를 [Copy to clipboard] 선택하여 복사하기

=>

build.gradle (:app-server)

ㄴ 복사해둔 코드 붙여넣기

=>

ㄴ 코끼리 아이콘 선택하여 Gralde reload 해주기

=>

ㄴ Project 선택

=>

ㄴ External Libraries > 라이브러리가 추가됨을 확인

 

 

AppConfig.java

ㄴ 해당 코드 복사하여 주석 처리 해두기

=>

https://mybatis.org/spring/getting-started

 

mybatis-spring –

Getting Started This chapter will show you in a few steps how to install and setup MyBatis-Spring and how to build a simple transactional application. Installation To use the MyBatis-Spring module, you just need to include the mybatis-spring-3.0.2.jar file

mybatis.org

=>

ㄴ 해당 코드 이용하기

=>
AppConfig.java

ㄴ SqlSessionFactory를 Spring 빈으로 구성하도록 함

 

 

 

https://docs.spring.io/spring-framework/reference/data-access/jdbc/connections.html#jdbc-DriverManagerDataSource

 

Controlling Database Connections :: Spring Framework

Spring obtains a connection to the database through a DataSource. A DataSource is part of the JDBC specification and is a generalized connection factory. It lets a container or a framework hide connection pooling and transaction management issues from the

docs.spring.io

 

=>

ㄴ DriverManagerDataSource 설정 확인

 

 

https://central.sonatype.com/

 

Maven Central

Official search by the maintainers of Maven Central Repository.

central.sonatype.com

 

ㄴ spring-jdbc 라이브러리 검색

=>

ㄴ spring-jdbc 6.0.11 버전 Gradle (short) 타입의 코드를 [Copy to clipboard] 선택하여 복사하기

=>
build.gradle

ㄴ 복사한 코드 붙여넣기

=>

ㄴ 코끼리 아이콘 선택하여 리로드

=>

ㄴ spring-jdbc 라이브러리 추가된 것을 확인

=>

AppConfig.java

ㄴ Spring의 JavaConfig 방식을 사용하고 있으며, @Bean 어노테이션을 통해 빈을 정의

ㄴ SqlSessionFactory 빈을 설정하는 부분에서 DataSource를 매개변수로 받도록 함

    ㄴ SqlSessionFactoryBean을 생성할 때 필요한 데이터 소스를 주입받기 위함
ㄴ DataSource 빈도 정의

    ㄴ DriverManagerDataSource를 사용하여 데이터 소스를 설정하고 반환하도록 함

    ㄴ 실제 데이터베이스 연결 정보를 설정하는 부분

=>

AppConfig.java

ㄴ @PropertySource 애노테이션을 이용하여 bitcamp/report/config/ncloud/jdbc.properties 클래스 패스 상에 위치한 jdbc.properties 파일을 로드하도록 함

=>
AppConfig.java

=>

ㄴ 이전에 찾아두었던 문서 참고

=>
AppConfig.java

ㄴ @Value 어노테이션을 사용하여 jdbc.driver, jdbc.url, jdbc.username, jdbc.password 프로퍼티 값을 주입하도록 함

=>

AppConfig.java

=>

AppConfig.java

ㄴ 주입받은 프로퍼티 값을 이용하여 DataSource 빈을 구성하고 반환하도록 함

     ㄴ 프로퍼티 파일에서 읽은 값으로 데이터베이스 연결을 설정

=>

AppConfig.java

ㄴ dataSource -> ds 로 변수명 변경

=>
mybatis-config.xml

ㄴ 해당 코드 제거

 

 

https://docs.spring.io/spring-framework/reference/data-access/jdbc/connections.html#jdbc-DriverManagerDataSource

 

Controlling Database Connections :: Spring Framework

Spring obtains a connection to the database through a DataSource. A DataSource is part of the JDBC specification and is a generalized connection factory. It lets a container or a framework hide connection pooling and transaction management issues from the

docs.spring.io

 

 

 

https://mybatis.org/spring/transactions.html

 

mybatis-spring –

Transactions One of the primary reasons for using MyBatis-Spring is that it allows MyBatis to participate in Spring transactions. Rather than create a new transaction manager specific to MyBatis, MyBatis-Spring leverages the existing DataSourceTransactionM

mybatis.org

 

 

 

AppConfig.java

ㄴ AppConfig 의 dataSource() 메서드가 호출될 경우 해당 문구를 출력하도록 함

=>
AppConfig.java

ㄴ 트랜잭션 관리 설정 추가

=>

mybatis-config.xml

ㄴ 해당 코드 제거

=>

mybatis-config.xml

ㄴ 해당 설정을 AppConfig 에서 해주도록 함

=>

AppConfig.java

=>

mybatis-config.xml

ㄴ 해당 코드 제거

=>
AppConfig.java

ㄴ "bitcamp.report.dao.mysql" 패키지 내에서 마이바티스 매퍼 인터페이스를 검색하고 스프링 빈으로 등록하도록 함

ㄴ @MapperScan 어노테이션은 마이바티스(MyBatis)와 관련이 있으며, 마이바티스 매퍼 인터페이스를 검색하고 이를 스프링 빈으로 등록하는 데 사용함

=>

mybatis-config.xml

ㄴ 해당 코드 제거

=>

ㄴ mybatis-config.xml 파일은 이제 필요 없으므로 삭제

=>

listener/MyServletRequestListener.java

ㄴ 해당 코드 주석 처리 해보기

=>

ㄴ App 실행하기

=>

ㄴ 로그인 선택

=>

ㄴ [로그인] 선택

=>

ㄴ 오류 발생

=>

AppConfig.java

ㄴ 일반적으로 스프링 애플리케이션에서는 PlatformTransactionManager를 사용하는 것이 더 일반적이며, 스프링의 다양한 트랜잭션 관리 전략과 더 쉽게 통합할 수 있음

ㄴ 그래서 일반적으로 PlatformTransactionManager 인터페이스를 사용하고 그에 해당하는 구현체인 DataSourceTransactionManager를 반환하는 것이 권장됨

=>

AppConfig.java

ㄴ bitcamp.report.dao.mysql -> bitcamp.report.dao

=>

AppConfig.java

ㄴ MyBatis 매퍼 XML 파일의 위치를 설정(이 위치는 매퍼 인터페이스와 SQL 매핑 파일을 찾기 위해 사용)

 

 

MySQLBoardDao.java

ㄴ @Component 애노테이션 주석 처리 해보기

=>

MySQLMemberDao.java

ㄴ @Component 애노테이션 주석 처리 해보기

=>

DispatcherServlet.java

ㄴ 해당 코드 제거

=>

App 재실행

=>

=>

AppConfig.java

ㄴ 매퍼 XML 파일이 여러 개인 경우에는 setMapperLocations 메서드를 사용하여 배열 또는 리스트로 여러 리소스를 지정할 수 있음

ㄴ Resource 객체들을 배열 또는 리스트로 생성하고 setMapperLocations를 통해 설정할 수 있음

=>

ㄴ App 재실행

=>

=>

=>

ㄴ 로그인 정상적으로 실행됨

=>

ㄴ 게시물 목록도 정상적으로 출력됨을 확인

=>

AppConfig.java

ㄴ 이렇게 배열로 지정할 수 있음

=>

AppConfig.java

ㄴ 이렇게 배열로 지정할 수 있음

=>

AppConfig.java

ㄴ getResources 를 이용해 여러 개의 XxxDao.xml 을 받을 수 있음

=>

ㄴ App 재실행

=>

ㄴ 게시글 목록 정상적으로 출력됨을 확인

=>

=>

ㄴ 로그인 정상적으로 실행됨을 확인

=>

ㄴ 로그아웃도 정상적으로 실행됨을 확인

=>

ㄴ 회원 목록도 정상적으로 출력됨을 확인

 

 

 

build.gradle(:app-common)

ㄴ app-common 의 build.gradle 스크립트 파일에서도 해당 라이브러리 추가해주도록 함

=>

ㄴ 리로드 해주기

 

 

BoardDao.xml

ㄴ 해당 쿼리 확인

=>

app-common/src/main/java/bitcamp/report/dao/BoardDao.java

ㄴ category와 no 두 개의 파라미터를 받으므로 @Param 애노테이션 이용하기

ㄴ SQL 쿼리에서 파라미터를 사용할 때 파라미터 이름을 사용하게 되는데, 이 이름을 지정하지 않으면 자동으로 파라미터의 이름을 사용함

    ㄴ category 파라미터를 SQL에서 categoryNo라는 이름으로 사용하도록 함

    ㄴ no 파라미터를 SQL에서 boardNo라는 이름으로 사용하도록 함

=>
BoardDao.xml

ㄴ 해당 코드 제거

ㄴ 이제 map 타입으로 받지 않음

=>

MemberDao.java

ㄴ 해당 쿼리 확인

=>

app-common/src/main/java/bitcamp/report/dao/MemberDao.java

ㄴ phone과 password 두 개의 파라미터를 받으므로 @Param 애노테이션 이용하기

ㄴ SQL 쿼리에서 파라미터를 사용할 때 파라미터 이름을 사용하게 되는데, 이 이름을 지정하지 않으면 자동으로 파라미터의 이름을 사용함

    ㄴ phone 파라미터를 SQL에서 phone 라는 이름으로 사용하도록 함

    ㄴ password 파라미터를 SQL에서 password 라는 이름으로 사용하도록 함

=>

MemberDao.xml

ㄴ 해당 코드 제거

ㄴ 이제 member 타입으로 받지 않음

 

 

 

ㄴ dao 패키지 자체를 삭제

 

 

LoginController.java

ㄴ 해당 코드 대신 아래와 같이 설정

=>

LoginController.java

ㄴ Member 객체를 받지 말고 phone 과 password 2개를 받도록 함

=>

LoginController.java

=>

ㄴ Member 객체가 아닌 2개의 값 phone, password 를 findByPhoneAndPassword 메서드에 넘겨주도록 함

=>

ㄴ App 재실행

=>

=>

=>

ㄴ 로그인 정상적으로 실행됨을 확인

=>

ㄴ 게시글 목록이 정상적으로 출력됨을 확인

=>

ㄴ 게시글 상세보기가 정상적으로 출력됨

 

 

 

ㄴ 해당 패키지에 있는 SqlSessionFactoryProxy.java 가 없어도 잘 실행되므로 해당 파일 삭제

 

 

 

BoardAddController.java

ㄴ SqlSessionFactor 대신 PlatformTransactionManager 이용하기

=>

ㄴ PlatformTransactionManager 이용 하기

ㄴ 해당 코드 복사

=>

BoardAddController.java

ㄴ 복사한 코드 붙여넣기

=>

BoardAddController.java

ㄴ 이름 수정하기

=>

BoardAddController.java

ㄴ sqlSessionFactory.openSession(false).commit();
    return "redirect:list?category=" + request.getParameter("category");

    이 코드 대신 위와 같이 TransactionManager 이용하기

=>

BoardAddController.java

ㄴ sqlSessionFactory.openSession(false).rollback(); 대신 txManager.rollback(status); 이용하기 (TransactionManager 이용하기)

=>

App 재실행

=>

=>

ㄴ 로그인 정상적으로 실행됨을 확인

=>

ㄴ 게시글 목록이 정상적으로 출력됨을 확인

=>

ㄴ 새 글 선택하여 내용 입력 후 [등록] 선택

=>

ㄴ 게시글이 정상적으로 등록됨을 확인

=>

=>

=>

ㄴ 크롬이 아닌 다른 브라우저에서 띄웠을 때(세션, 쿠키 다름)에도 등록된 게시글이 정상적으로 출력됨을 확인

 

 

다른 Controller 도 같은 방식으로 변경해주기

BoardDetailController.java

package bitcamp.report.controller;

import bitcamp.report.dao.BoardDao;
import bitcamp.report.vo.Board;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component("/board/detail")
public class BoardDetailController implements PageController {

  BoardDao boardDao;
  PlatformTransactionManager txManager;

  public BoardDetailController(BoardDao boardDao, PlatformTransactionManager txManager) {
    this.boardDao = boardDao;
    this.txManager = txManager;
  }

  @Override
  public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception {

    DefaultTransactionDefinition def = new DefaultTransactionDefinition();
    def.setName("tx1");
    def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
    TransactionStatus status = txManager.getTransaction(def);

    try {
      int category = Integer.parseInt(request.getParameter("category"));
      int no = Integer.parseInt(request.getParameter("no"));

      Board board = boardDao.findBy(category, no);
      if (board != null) {
        board.setViewCount(board.getViewCount() + 1);
        boardDao.updateCount(board);
        txManager.commit(status);
        request.setAttribute("board", board);

      }
      return "/WEB-INF/jsp/board/detail.jsp";

    } catch (Exception e) {
      txManager.rollback(status);
      request.setAttribute("refresh", "2;url=/board/list?category=" + request.getParameter("category"));
      throw e;
    }
  }
}

 

 

BoardUpdateController.java

package bitcamp.report.controller;

import bitcamp.report.dao.BoardDao;
import bitcamp.report.service.NcpObjectStorageService;
import bitcamp.report.vo.AttachedFile;
import bitcamp.report.vo.Board;
import bitcamp.report.vo.Member;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.util.ArrayList;

@Component("/board/update")
public class BoardUpdateController implements PageController {

  BoardDao boardDao;
  PlatformTransactionManager txManager;
  NcpObjectStorageService ncpObjectStorageService;

  public BoardUpdateController(BoardDao boardDao, PlatformTransactionManager txManager, NcpObjectStorageService ncpObjectStorageService) {
    this.boardDao = boardDao;
    this.txManager = txManager;
    this.ncpObjectStorageService = ncpObjectStorageService;
  }

  @Override
  public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
    Member loginUser = (Member) request.getSession().getAttribute("loginUser");
    if (loginUser == null) {
      request.getParts(); // 일단 클라이언트가 보낸 파일을 읽는다. 그래야 응답 가능!
      return "redirect:../auth/login";
    }

    DefaultTransactionDefinition def = new DefaultTransactionDefinition();
    def.setName("tx1");
    def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
    TransactionStatus status = txManager.getTransaction(def);

    try {
      Board board = new Board();
      board.setWriter(loginUser);
      board.setNo(Integer.parseInt(request.getParameter("no")));
      board.setTitle(request.getParameter("title"));
      board.setContent(request.getParameter("content"));
      board.setCategory(Integer.parseInt(request.getParameter("category")));

      ArrayList<AttachedFile> attachedFiles = new ArrayList<>();
      for (Part part : request.getParts()) {
        if (part.getName().equals("files") && part.getSize() > 0) {
          String uploadFileUrl = ncpObjectStorageService.uploadFile(
                  "bitcamp-nc7-bucket-25", "board/", part);
          AttachedFile attachedFile = new AttachedFile();
          attachedFile.setFilePath(uploadFileUrl);
          attachedFiles.add(attachedFile);
        }
      }
      board.setAttachedFiles(attachedFiles);

      if (boardDao.update(board) == 0) {
        throw new Exception("게시글이 없거나 변경 권한이 없습니다.");
      } else {
        if (attachedFiles.size() > 0) {
          boardDao.insertFiles(board);
        }
        txManager.commit(status);
        return "redirect:list?category=" + request.getParameter("category");
      }

    } catch (Exception e) {
      txManager.rollback(status);
      request.setAttribute("refresh", "2;url=detail?category=" + request.getParameter("category") +
              "&no=" + request.getParameter("no"));
      throw e;
    }
  }

}

 

 

BoardDeleteController.java

package bitcamp.report.controller;

import bitcamp.report.dao.BoardDao;
import bitcamp.report.vo.Board;
import bitcamp.report.vo.Member;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component("/board/delete")
public class BoardDeleteController implements PageController {

  BoardDao boardDao;
  PlatformTransactionManager txManager;

  public BoardDeleteController(BoardDao boardDao, PlatformTransactionManager txManager) {
    this.boardDao = boardDao;
    this.txManager = txManager;
  }

  @Override
  public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception {

    Member loginUser = (Member) request.getSession().getAttribute("loginUser");
    if (loginUser == null) {
      return "redirect:../auth/login";
    }

    DefaultTransactionDefinition def = new DefaultTransactionDefinition();
    def.setName("tx1");
    def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
    TransactionStatus status = txManager.getTransaction(def);

    try {
      Board b = new Board();
      b.setNo(Integer.parseInt(request.getParameter("no")));
      b.setWriter(loginUser);
      b.setCategory(Integer.parseInt(request.getParameter("category")));

      boardDao.deleteFiles(b.getNo());

      if (boardDao.delete(b) == 0) {
        throw new Exception("해당 번호의 게시글이 없거나 삭제 권한이 없습니다.");
      } else {
        txManager.commit(status);
        return "redirect:list?category=" + request.getParameter("category");
      }

    } catch (Exception e) {
      txManager.rollback(status);
      request.setAttribute("refresh", "2;url=list?category=" + request.getParameter("category"));
      throw e;
    }
  }
}

 

 

BoardFileDeleteController.java

package bitcamp.report.controller;

import bitcamp.report.dao.BoardDao;
import bitcamp.report.vo.AttachedFile;
import bitcamp.report.vo.Board;
import bitcamp.report.vo.Member;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component("/board/fileDelete")
public class BoardFileDeleteController implements PageController {

  BoardDao boardDao;
  PlatformTransactionManager txManager;

  public BoardFileDeleteController(BoardDao boardDao, PlatformTransactionManager txManager) {
    this.boardDao = boardDao;
    this.txManager = txManager;
  }

  @Override
  public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception {

    Member loginUser = (Member) request.getSession().getAttribute("loginUser");
    if (loginUser == null) {
      return "redirect:../auth/login";
    }

    Board board = null;

    DefaultTransactionDefinition def = new DefaultTransactionDefinition();
    def.setName("tx1");
    def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
    TransactionStatus status = txManager.getTransaction(def);

    try {
      int category = Integer.parseInt(request.getParameter("category"));
      int fileNo = Integer.parseInt(request.getParameter("no"));

      AttachedFile attachedFile = boardDao.findFileBy(fileNo);
      board = boardDao.findBy(category, attachedFile.getBoardNo());
      if (board.getWriter().getNo() != loginUser.getNo()) {
        throw new Exception("게시글 변경 권한이 없습니다!");
      }

      if (boardDao.deleteFile(fileNo) == 0) {
        throw new Exception("해당 번호의 첨부파일이 없거나 삭제 권한이 없습니다.");
      } else {
        txManager.commit(status);
        return "redirect:detail?category=" + category + "&no=" + board.getNo();
      }

    } catch (Exception e) {
      txManager.rollback(status);
      request.setAttribute("refresh", "2;url=detail?category=" + request.getParameter("category") +
              "&no=" + board.getNo());
      throw e;
    }
  }
}

 

 

MemberAddController.java

package bitcamp.report.controller;

import bitcamp.report.dao.MemberDao;
import bitcamp.report.service.NcpObjectStorageService;
import bitcamp.report.vo.Member;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;

@Component("/member/add")
public class MemberAddController implements PageController {

  MemberDao memberDao;
  PlatformTransactionManager txManager;
  NcpObjectStorageService ncpObjectStorageService;

  public MemberAddController(MemberDao memberDao, PlatformTransactionManager txManager, NcpObjectStorageService ncpObjectStorageService) {
    this.memberDao = memberDao;
    this.txManager = txManager;
    this.ncpObjectStorageService = ncpObjectStorageService;
  }

  @Override
  public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
    if (request.getMethod().equals("GET")) {
      return "/WEB-INF/jsp/member/form.jsp";
    }

    DefaultTransactionDefinition def = new DefaultTransactionDefinition();
    def.setName("tx1");
    def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
    TransactionStatus status = txManager.getTransaction(def);

    try {
      Member m = new Member();
      m.setName(request.getParameter("name"));
      m.setPhone(request.getParameter("phone"));
      m.setPassword(request.getParameter("password"));
      m.setPosition(request.getParameter("position").charAt(0));

      Part photoPart = request.getPart("photo");
      if (photoPart.getSize() > 0) {
        String uploadFileUrl = ncpObjectStorageService.uploadFile(
                "bitcamp-nc7-bucket-25", "member/", photoPart);
        m.setPhoto(uploadFileUrl);
      }
      memberDao.insert(m);
      txManager.commit(status);
      return "redirect:list";

    } catch (Exception e) {
      txManager.rollback(status);
      request.setAttribute("message", "회원 등록 오류!");
      request.setAttribute("refresh", "2;url=list");
      throw e;
    }
  }
}

 

 

MemberDeleteController.java

package bitcamp.report.controller;

import bitcamp.report.dao.MemberDao;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component("/member/delete")
public class MemberDeleteController implements PageController {

  MemberDao memberDao;
  PlatformTransactionManager txManager;

  public MemberDeleteController(MemberDao memberDao, PlatformTransactionManager txManager) {
    this.memberDao = memberDao;
    this.txManager = txManager;
  }

  @Override
  public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception {

    DefaultTransactionDefinition def = new DefaultTransactionDefinition();
    def.setName("tx1");
    def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
    TransactionStatus status = txManager.getTransaction(def);

    try {
      if (memberDao.delete(Integer.parseInt(request.getParameter("no"))) == 0) {
        throw new Exception("해당 번호의 회원이 없습니다!");
      } else {
        txManager.commit(status);
        return "redirect:list";
      }

    } catch (Exception e) {
      txManager.rollback(status);
      request.setAttribute("refresh", "2;url=list");
      throw e;
    }
  }

}

 

 

MemberUpdateController.java

package bitcamp.report.controller;

import bitcamp.report.dao.MemberDao;
import bitcamp.report.service.NcpObjectStorageService;
import bitcamp.report.vo.Member;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;

@Component("/member/update")
public class MemberUpdateController implements PageController {

  MemberDao memberDao;
  PlatformTransactionManager txManager;
  NcpObjectStorageService ncpObjectStorageService;

  public MemberUpdateController(MemberDao memberDao, PlatformTransactionManager txManager, NcpObjectStorageService ncpObjectStorageService) {
    this.memberDao = memberDao;
    this.txManager = txManager;
    this.ncpObjectStorageService = ncpObjectStorageService;
  }

  @Override
  public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception {

    DefaultTransactionDefinition def = new DefaultTransactionDefinition();
    def.setName("tx1");
    def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
    TransactionStatus status = txManager.getTransaction(def);

    try {
      Member member = new Member();
      member.setNo(Integer.parseInt(request.getParameter("no")));
      member.setName(request.getParameter("name"));
      member.setPhone(request.getParameter("phone"));
      member.setPassword(request.getParameter("password"));
      member.setPosition(request.getParameter("position").charAt(0));

      Part photoPart = request.getPart("photo");
      if (photoPart.getSize() > 0) {
        String uploadFileUrl = ncpObjectStorageService
            .uploadFile("bitcamp-nc7-bucket-25", "member/", photoPart);
        member.setPhoto(uploadFileUrl);
      }


      if (memberDao.update(member) == 0) {
        throw new Exception("회원이 없습니다.");
      } else {
        txManager.commit(status);
        return "redirect:list";
      }

    } catch (Exception e) {
      txManager.rollback(status);
      request.setAttribute("refresh", "2;url=list");
      throw e;
    }
  }

}

 

 

AppConfig.java

ㄴ 해당 코드 제거

=>

=>

ㄴ 로그인 성공적으로 실행됨을 확인

=>

ㄴ 회원목록이 정상적으로 출력됨을 확인

=>

=>

=>

오류 발생

=>
member/detail.jsp

ㄴ 회원 상세보기 오류 해결

=>

ㄴ 회원이 정상적으로 출력됨을 확인

 

 

 

ㄴ 게시글 목록이 정상적으로 출력됨을 확인

=>

=>

ㄴ 게시글이 정상적으로 등록됨을 확인

 

 

 

Logging 설정

https://mybatis.org/mybatis-3/logging.html

 

mybatis – MyBatis 3 | Logging

Logging MyBatis provides logging information through the use of an internal log factory. The internal log factory will delegate logging information to one of the following log implementations: SLF4J Apache Commons Logging Log4j 2 Log4j (deprecated since 3.

mybatis.org

ㄴ Logging 설정 방법 확인

ㄴ 해당 코드 복사하기

=>

AppConfig.java

ㄴ 복사한 코드 붙여넣기

ㄴ 해당 코드 추가하지 않아도 됨 (log4j2.xml 파일 추가 시 해당 설정 필요 없음)

 

 

https://central.sonatype.com/artifact/org.apache.logging.log4j/log4j-core/2.20.0

 

Maven Central: org.apache.logging.log4j:log4j-core:2.20.0

Discover log4j-core in the org.apache.logging.log4j namespace. Explore metadata, contributors, the Maven POM file, and more.

central.sonatype.com

 

=>

ㄴ 2.20.0 버전 선택

=>

ㄴ [Copy to clipboard] 선택

=>

bulid.gradle (:app-server)

ㄴ build.gradle 스크립트 파일의 dependencies 에 Log4j 2.x 버전 라이브러리 추가

=>

ㄴ 코끼리 아이콘 선택하여 gradle build 하기

=>

=>

ㄴ External Libraries 선택하여 log4j 2.x 버전 라이브러리 추가됨을 확인

=>

=>

ㄴ resources 폴더에 log4j2.xml 파일 추가

=>

log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">

    <!-- 로그 출력 형태를 정의한다. -->
    <Appenders>

        <!-- 표준 출력 장치인 콘솔로 출력하는 방식을 정의한다. -->
        <Console name="stdout" target="SYSTEM_OUT">
            <PatternLayout pattern="[%-5level] %d{yyyy-MM-dd} [%t] %c{1} - %msg%n" />
        </Console>

        <!--
        <File name="file" fileName="./logs/file/sample.log" append="false">
          <PatternLayout pattern="[%-5level] %d{yyyy-MM-dd} [%t] %c{1} - %msg%n" />
        </File>
        -->
    </Appenders>

    <!-- 로그 출력을 적용할 대상과 로그 출력 레벨을 지정한다. -->
    <Loggers>
        <Logger name="bitcamp.report.dao" level="debug" additivity="false">
            <AppenderRef ref="stdout" />
        </Logger>

        <Logger name="bitcamp.report" level="debug" additivity="false">
            <AppenderRef ref="stdout" />
        </Logger>

        <!-- Root => 모든 대상에 적용할 기본 로그 출력 형식과 레벨 -->
        <Root level="warn" additivity="false">
            <AppenderRef ref="stdout" /> <!-- 로그를 출력할 때 사용할 출력 방식 지정 -->
        </Root>
    </Loggers>
</Configuration>

=>

App 실행

=>

ㄴ [Clear All] 선택

=>

=>

=>

ㄴ 로그가 정상적으로 출력됨을 확인

=>

servlet/DispatcherServlet.java

ㄴ Spring IoC 컨테이너에서 등록된 모든 빈의 이름과 클래스 이름을 출력해보기

=>

App 실행

=>

=>

servlet/DispatcherServlet.java

ㄴ 주석 처리해주기