KH 정보교육원 [ Java ]
KH 116일차 - Spring ( 게시판 만들기 - 영속성 / 비지니스 계층 ) (****)
monimoni
2022. 8. 11. 15:39
[ 1. 게시판 만들기 - 영속성 계층 ] (*****)
[ 1 - 1. VO 클래스 만들기 ]
더보기
[ + 코드 보기 ]
package org.zerock.myapp.domain;
import lombok.Value;
@Value
public class BoardVO {
private Integer bno;
private String title;
private String content;
private String writer;
} // end class
[ 1 - 2. CRUD 기능을 할 Mapper만들기 ]
더보기
[ + 코드 보기 ]
package org.zerock.myapp.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.zerock.myapp.domain.BoardVO;
import org.zerock.myapp.exception.DAOException;
// + 이 자바 인터페이스가 영속성 계층의 DAO 역할을 할
// + 마이바티스의 Mapper Interface로서의 역할을 하도록 구현
// + Mapper에서 CRUD하게 해준다. (***)
public interface BoardMapper {
// ====================================================================
// + 1. 게시물 전체 목록 출력하기
// ====================================================================
// + /*+ index_desc( 테이블명 indexName ) */는 오라클 힌트로 원래는 indexName을 지정해줘야 하지만,
// + 지정해주지 않으면 테이블(tbl_board)의 PK를 기준으로 desc 내림차순으로 정렬해준다.
// + INDEX (INDEX_ASC) : 오름차순 정렬, INDEX_DESC : 내림차순 정렬
// + 같은 기능으로 ORDER BY가 있지만, 시스템에 부담이 되는 기능이기에 사용을 지양해줘야 한다.
// ====================================================================
@Select("SELECT /*+ index_desc(tbl_board) */ * FROM tbl_board WHERE bno > 0")
public abstract List<BoardVO> selectAllList() throws DAOException;
// ====================================================================
// + 2. 새로운 게시물 등록 -> Mapper XMl (***)
// ====================================================================
// + 게시물 등록에 필요한 데이터가 VO 객체로 들어왔기 때문에,
// + @Param(바인드 변수명) 어노테이션과 @Insert(SQL문) 어노테이션으로 처리가 힘들다.
// + 그렇기에 Mapper XML 파일에 SQL을 등록해서 처리하는 것이 좋다.
// ====================================================================
// + MyBatis 프레임 워크의 Mapper SQL의 자동실행규칙 :
// ====================================================================
// + 1 ) Mapper Interface가 소속된 패키지와 동일한 폴더구조를 CLASSPATH 아래에 만들어라
// + CLASSPATH = src/main/java --> /WEB-INF/classes/ ( 이 폴더가 CLASSPATH의 시작점이다. )
// + CLASSPATH = src/main/resources --> /WEB-INF/classes/ ( 이 폴더가 CLASSPATH의 시작점이다. )
// + --> ex. src/main/resources/org/zerock/myapp/mapper
// ====================================================================
// + 2 ) 위에서 만든 폴더구조에서 Mapper Interface의 타입명과 동일한 이름으로 Mapper XML 파일 생성
// + ex. 이 경우 BoardMapper라는 이름의 Mapper Interface이기에, BoardMapper.xml파일을 만들어야 한다.
// ====================================================================
// + 3 ) Mapper XML 파일에서 namespace의 이름을 Mapper Interface의 FQCN으로 지정한다.
// + ex. 이 경우에는 namespace에 org.zerock.myapp.mapper.BoardMapper을 지정한다.
// ====================================================================
// + 4 ) SQL문장을 저장할 태그의 id(sqlId)값을 Mapper Interface 내에서 호출할 추상메소드의 이름과 동일하게 한다.
// + ex. 이 경우에는 추상메소드의 이름이 insert이기에 id에 insert를 지정해 준다.
// ====================================================================
// @Insert("INSERT INTO tbl_board (bno, title, content, writer) VALUES ( ?, ?, ?, ? )")
public abstract Integer insert(BoardVO vo) throws DAOException; // insert
public abstract Integer insertSelectKey(BoardVO vo) throws DAOException;
// ====================================================================
// + 3. 게시물 삭제 -> @Delete + @Param ( #{ 바인드변수명 } )
// ====================================================================
// + Integer bno -> @Param("bno") -> #{bno}순으로 값이 전달된다.
// ====================================================================
@Delete("DELETE FROM tbl_board WHERE bno = #{bno}")
public abstract Integer delete(@Param("bno")Integer bno) throws DAOException;
// ====================================================================
// + 4. 게시물 수정 -> Mapper XMl (***)
// ====================================================================
public abstract Integer update(BoardVO vo) throws DAOException;
// ====================================================================
// + 5. 특정한 게시물 출력 ( SELECT )
// ====================================================================
// + 칼럼의 순서는 VO 객체의 순서를 따라야 한다. (***)
// + 그렇기에 VO는 테이블의 스키마를 보고 그대로 따라하는 것이 좋다.
// ====================================================================
@Select("SELECT bno, title, content, writer FROM tbl_board WHERE bno = #{bno}")
public abstract BoardVO select(@Param("bno") Integer bno) throws DAOException;
} // end interface
[ 1 - 3. Mapper XML 파일 만들기 ( 자동실행규칙 따르기 ) ] (***)
더보기
[ + 코드 보기 ]
<?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="org.zerock.myapp.mapper.BoardMapper">
<insert id="insert">
INSERT INTO tbl_board(bno, title, content, writer)
VALUES( #{bno}, #{title}, #{content}, #{writer} )
</insert>
<insert id="insertSelectKey">
<!-- 이는 bno는 시퀀스로 자동으로 얻어지는 값인데, -->
<!-- 이를 order=before로 하여 미리 값을 얻어가지고 이를 bno에 넘겨준다는 의미이다. -->
<selectKey keyProperty="bno" order="BEFORE" resultType="int">
SELECT "ADMIN"."ISEQ$$_93174".nextval FROM dual
</selectKey>
INSERT INTO tbl_board(bno, title, content, writer)
VALUES( #{bno}, #{title}, #{content}, #{writer} )
</insert>
<update id="update">
UPDATE tbl_board
SET
title = #{title},
content = #{content},
writer = #{writer}
WHERE bno = #{bno}
</update>
</mapper>
[ + Mapper Interface 위치 ]
[ 1 - 4. Test 하기 ]
더보기
[ + 코드 보기 ]
package org.zerock.myapp.mapper;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.Timeout;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.zerock.myapp.domain.BoardVO;
import org.zerock.myapp.exception.DAOException;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.extern.log4j.Log4j2;
@Log4j2
@NoArgsConstructor
// JUNIT 5
@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations = {
"file:src/main/webapp/WEB-INF/spring/root-context.xml",
"file:src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml"})
@TestInstance(Lifecycle.PER_CLASS)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class BoardMapperTests {
// =======================================================================
// @Autowired
@Setter(onMethod_= {@Autowired})
private BoardMapper mapper;
// =======================================================================
@BeforeAll
void beforeAll() {
log.trace("beforeAll() invoked.");
assertNotNull(this.mapper);
log.info("\t + 1. this.mapper : {}", this.mapper);
// + 1. this.mapper : org.apache.ibatis.binding.MapperProxy@2616b618
log.info("\t + 2. type : {}", this.mapper.getClass().getName());
// + 2. type : com.sun.proxy.$Proxy48
} // beforeAll
// =======================================================================
@Disabled
@Test
@Order(1)
@DisplayName("1. BoardMapper.getList() test")
@Timeout(value=5, unit = TimeUnit.SECONDS)
void testGetList() throws DAOException {
log.trace("testGetList() invoked.");
List<BoardVO> list = this.mapper.selectAllList();
// + 오라클 힌트로 내림차순하였기에, PK를 기준으로 내림차순 정렬된다.
// + mapper에 있는 @Select가 실행된다.
Objects.requireNonNull(list);
list.forEach(log::info);
} //testGetList
// =======================================================================
@Disabled
@Test
@Order(2)
@DisplayName("2. BoardMapper.testDelete() test")
@Timeout(value=5, unit = TimeUnit.SECONDS)
void testDelete() throws DAOException {
log.trace("testDelete() invoked.");
int bno = 30;
int affectedLines = this.mapper.delete(bno);
log.info("\t + affectedLines : {}", affectedLines);
assert affectedLines == 1;
} //testDelete
// =======================================================================
@Disabled
@Test
@Order(3)
@DisplayName("3. BoardMapper.testSelect() test")
@Timeout(value=5, unit = TimeUnit.SECONDS)
void testSelect() throws DAOException {
log.trace("testSelect() invoked.");
int bno = 45;
BoardVO vo = this.mapper.select(bno);
Objects.requireNonNull(vo);
log.info("\t + vo : {}", vo);
} //testSelect
// =======================================================================
@Disabled
@Test
@Order(4)
@DisplayName("4. BoardMapper.testInsert(BoardVO) test")
@Timeout(value=5, unit = TimeUnit.SECONDS)
void testInsert() throws DAOException {
log.trace("testInsert() invoked.");
// 1. BoardVO 객체 생성 : SQL 바인드 변수들에게 넘겨줄 파라미터
BoardVO vo = new BoardVO(555, "TITLE_555","CONTENT_555","WRITER_555");
log.info("\t + 1. vo : {}", vo);
// 2. Mapper Interface의 추상 메소드 호출
int affectedLined = this.mapper.insert(vo);
log.info("\t + 2. affectedLined : {}", affectedLined);
// 3. 입력 검증
assert affectedLined == 1;
} // testInsert
// =======================================================================
@Disabled
@Test
@Order(5)
@DisplayName("5. BoardMapper.testInsertSelectKey(BoardVO) test")
@Timeout(value=5, unit = TimeUnit.SECONDS)
void testInsertSelectKey() throws DAOException {
log.trace("testInsertSelectKey() invoked.");
// 1. BoardVO 객체 생성 : SQL 바인드 변수들에게 넘겨줄 파라미터
// + tbl_board 테이블에서 bno는 null이거나 값을 넣지 않으면 시퀀스를 통해서 자동생성되고 있는데,
// + 이때 시퀀스에서 값을 얻어내서 bno로 대입하기 위해서는
// + BoradMapper.xml파일에서 selectkey 태그 내에서 시퀀스를 지정해줘서 값을 미리 얻어내야 한다.(***)
BoardVO vo = new BoardVO( null, "TITLE_555","CONTENT_555","WRITER_555");
log.info("\t + 1. vo : {}", vo);
// 2. Mapper Interface의 추상 메소드 호출
int affectedLined = this.mapper.insertSelectKey(vo);
log.info("\t + 2. affectedLined : {}", affectedLined);
log.info("\t + 3. after method invoked vo : {}", vo);
// 3. 입력 검증
assert affectedLined == 1;
} // testInsertSelectKey
// =======================================================================
// @Disabled
@Test
@Order(6)
@DisplayName("6. BoardMapper.testupdate(BoardVO) test")
@Timeout(value=5, unit = TimeUnit.SECONDS)
void testupdate() throws DAOException {
log.trace("testupdate() invoked.");
// 1. BoardVO 객체 생성 : SQL 바인드 변수들에게 넘겨줄 파라미터
BoardVO vo = new BoardVO(555, "TITLE_MODIFIED","CONTENT_MODIFIED","WRITER_MODIFIED");
log.info("\t + 1. vo : {}", vo);
// 2. Mapper Interface의 추상 메소드 호출
int affectedLined = this.mapper.update(vo);
log.info("\t + 2. affectedLined : {}", affectedLined);
// 3. 입력 검증
assert affectedLined == 1;
} // testInsert
} // end class
[ 2. 게시판 만들기 - service : 비지니스 영역 ] (****)
[ + 위의 Mapper을 그대로 사용하고 있습니다. ]
더보기
[ + 코드 보기 ]
package org.zerock.myapp.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.zerock.myapp.domain.BoardVO;
import org.zerock.myapp.exception.DAOException;
// + 이 자바 인터페이스가 영속성 계층의 DAO 역할을 할
// + 마이바티스의 Mapper Interface로서의 역할을 하도록 구현
// + Mapper에서 CRUD하게 해준다. (***)
public interface BoardMapper {
// ====================================================================
// + 1. 게시물 전체 목록 출력하기
// ====================================================================
// + /*+ index_desc( 테이블명 indexName ) */는 오라클 힌트로 원래는 indexName을 지정해줘야 하지만,
// + 지정해주지 않으면 테이블(tbl_board)의 PK를 기준으로 desc 내림차순으로 정렬해준다.
// + INDEX (INDEX_ASC) : 오름차순 정렬, INDEX_DESC : 내림차순 정렬
// + 같은 기능으로 ORDER BY가 있지만, 시스템에 부담이 되는 기능이기에 사용을 지양해줘야 한다.
// ====================================================================
@Select("SELECT /*+ index_desc(tbl_board) */ * FROM tbl_board WHERE bno > 0")
public abstract List<BoardVO> selectAllList() throws DAOException;
// ====================================================================
// + 2. 새로운 게시물 등록 -> Mapper XMl (***)
// ====================================================================
// + 게시물 등록에 필요한 데이터가 VO 객체로 들어왔기 때문에,
// + @Param(바인드 변수명) 어노테이션과 @Insert(SQL문) 어노테이션으로 처리가 힘들다.
// + 그렇기에 Mapper XML 파일에 SQL을 등록해서 처리하는 것이 좋다.
// ====================================================================
// + MyBatis 프레임 워크의 Mapper SQL의 자동실행규칙 :
// ====================================================================
// + 1 ) Mapper Interface가 소속된 패키지와 동일한 폴더구조를 CLASSPATH 아래에 만들어라
// + CLASSPATH = src/main/java --> /WEB-INF/classes/ ( 이 폴더가 CLASSPATH의 시작점이다. )
// + CLASSPATH = src/main/resources --> /WEB-INF/classes/ ( 이 폴더가 CLASSPATH의 시작점이다. )
// + --> ex. src/main/resources/org/zerock/myapp/mapper
// ====================================================================
// + 2 ) 위에서 만든 폴더구조에서 Mapper Interface의 타입명과 동일한 이름으로 Mapper XML 파일 생성
// + ex. 이 경우 BoardMapper라는 이름의 Mapper Interface이기에, BoardMapper.xml파일을 만들어야 한다.
// ====================================================================
// + 3 ) Mapper XML 파일에서 namespace의 이름을 Mapper Interface의 FQCN으로 지정한다.
// + ex. 이 경우에는 namespace에 org.zerock.myapp.mapper.BoardMapper을 지정한다.
// ====================================================================
// + 4 ) SQL문장을 저장할 태그의 id(sqlId)값을 Mapper Interface 내에서 호출할 추상메소드의 이름과 동일하게 한다.
// + ex. 이 경우에는 추상메소드의 이름이 insert이기에 id에 insert를 지정해 준다.
// ====================================================================
// @Insert("INSERT INTO tbl_board (bno, title, content, writer) VALUES ( ?, ?, ?, ? )")
public abstract Integer insert(BoardVO vo) throws DAOException; // insert
public abstract Integer insertSelectKey(BoardVO vo) throws DAOException;
// ====================================================================
// + 3. 게시물 삭제 -> @Delete + @Param ( #{ 바인드변수명 } )
// ====================================================================
// + Integer bno -> @Param("bno") -> #{bno}순으로 값이 전달된다.
// ====================================================================
@Delete("DELETE FROM tbl_board WHERE bno = #{bno}")
public abstract Integer delete(@Param("bno")Integer bno) throws DAOException;
// ====================================================================
// + 4. 게시물 수정 -> Mapper XMl (***)
// ====================================================================
public abstract Integer update(BoardVO vo) throws DAOException;
// ====================================================================
// + 5. 특정한 게시물 출력 ( SELECT )
// ====================================================================
// + 칼럼의 순서는 VO 객체의 순서를 따라야 한다. (***)
// + 그렇기에 VO는 테이블의 스키마를 보고 그대로 따라하는 것이 좋다.
// ====================================================================
@Select("SELECT bno, title, content, writer FROM tbl_board WHERE bno = #{bno}")
public abstract BoardVO select(@Param("bno") Integer bno) throws DAOException;
} // end interface
[ 2 - 1. Service Exception 구현 ]
더보기
[ + 코드 보기 ]
package org.zerock.myapp.exception;
// + 비지니스 계층의 모든 종류의 Service 구현에서 발생하는 예외를 의미
public class ServiceException extends Exception {
private static final long serialVersionUID = 1L;
public ServiceException(String message) {
super(message);
} // constructor
public ServiceException(Exception e) {
super(e);
} //constructor
} // end class
[ 2 - 2. Service Interface 구현 ]
더보기
[ + 코드 보기 ]
package org.zerock.myapp.service;
import java.util.List;
import org.zerock.myapp.domain.BoardVO;
import org.zerock.myapp.exception.ServiceException;
// + 롬복은 인터페이스에서 사용이 불가능하다.
// + 비지니스 로직은 Service에서 구현되어야 한다.
public interface BoardService {
// + 1. 게시파 전체 목록을 조회하여 리스트로 반환해주는 메소드
public abstract List <BoardVO> getAllList() throws ServiceException;
// + 2. 게시판에서 특정 게시물 조회
public abstract BoardVO get(Integer bno) throws ServiceException;
// + 3. 게시판에서 게시물 삭제 요청 처리
public abstract Integer remove(Integer bno) throws ServiceException;
// + 4. 게시판에서 게시물 수정 요청 처리
public abstract Integer update(BoardVO vo) throws ServiceException;
// + 5. 게시판에서 새로운 게시물 추가 요청 처리
public abstract Integer add(BoardVO vo) throws ServiceException;
// + 6. 게시판에서 새로운 게시물 추가 요청 처리 2
public abstract Integer addAuto(BoardVO vo) throws ServiceException;
} // end interface
[ 2 - 3. Service Interface를 root-context.xml파일에서 Bean으로 등록하기 ]
더보기
[ + 코드 보기 ]
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
xsi:schemaLocation="http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- Root Context: defines shared resources visible to all other web components -->
<!-- 1. Hikari Configuration -->
<!-- primary는 같은 이름이 여러개 일때, 이것을 우선순위로 삼으라는 의미이다. -->
<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig" primary="true" >
<description>HikariCP Configuration</description>
<!-- 1. JDBC 연결정보 속성들에 값 설정 -->
<property name="driverClassName" value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy" />
<property name="jdbcUrl" value="jdbc:log4jdbc:oracle:thin:@db25000000_high?TNS_ADMIN=C:/opt/OracleCloudWallet/ATP"/>
<property name="username" value="ADMIN"/>
<property name="password" value="Oracle00000"/>
<!-- 2. Connection Pool의 작동방식과 관련된 속성들에 값 설정 -->
<property name="maximumPoolSize" value="10"/>
<property name="minimumIdle" value="2"/>
<property name="idleTimeout" value="10000"/>
<property name="connectionTimeout" value="1000"/>
<property name="connectionTestQuery" value="SELECT 1 FROM dual"/>
<property name="dataSourceJNDI" value="jdbc/HikariCP"/>
<property name="poolName" value="*** HikariDataSource ***"/>
</bean>
<!-- 2. hikariDataSource -->
<bean
id="hikariDataSource"
class="com.zaxxer.hikari.HikariDataSource"
destroy-method="close"
primary="false">
<description>HikariCP DataSource</description>
<constructor-arg ref="hikariConfig"/>
<!-- ref : 매개변수로 hikariConfig를 사용 -->
</bean>
<!-- MyBatis Configuration -->
<!-- 3. SqlSessionFactory Bean 등록 -->
<bean
id="sqlSessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 1 ) SqlSessionFactory Bean에서 히카리데이터 소스 사용 -->
<!-- <property name="dataSource" ref="hikariDataSource" /> -->
<!-- 2 ) SqlSessionFactory Bean에서 마이바티스의 pooledDataSource를 사용 -->
<property name="dataSource" ref="pooledDataSource" />
<property name="configLocation" value="classpath:mybatis-config.xml" />
<!-- classpath:는 resources 객체를 만들어 준다. -->
<!-- classpath는 path 환경변수에 등록한 순서대로 파일을 찾아준다. -->
<!-- classpath: = /WEB-INF/classes/..... -->
</bean>
<!-- 4. MyBatis 커넥션 풀 -->
<bean
id="pooledDataSource"
class="org.apache.ibatis.datasource.pooled.PooledDataSource"
destroy-method="forceCloseAll"
primary="true">
<description>MyBatis Pooled Data Source</description>
<!-- 1. MyBatis 커넥션 풀 연결정보 속성들에 값 설정 -->
<property name="driver" value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy" />
<property name="url" value="jdbc:log4jdbc:oracle:thin:@db2500000_high?TNS_ADMIN=C:/opt/OracleCloudWallet/ATP"/>
<property name="username" value="ADMIN"/>
<property name="password" value="Oracle000000"/>
<!-- 2. MyBatis Connection Pool의 작동방식과 관련된 속성들에 값 설정 -->
<property name="poolMaximumActiveConnections" value="5"/>
<property name="poolMaximumIdleConnections" value="2"/>
<property name="poolPingEnabled" value="true"/>
<property name="poolPingQuery" value="SELECT 1 FROM dual"/>
<property name="loginTimeout" value="1"/>
</bean>
<!-- mybatis와 spring을 연동했을 때에만 사용이 가능한 태그 -->
<!-- mapper 자체를 bean으로 등록시켜 버린다. -->
<!-- <mybatis-spring:scan base-package="org.zerock.myapp.mapper" /> -->
<context:component-scan base-package="org.zerock.myapp.service"></context:component-scan>
</beans>
[ 2 - 4. Service Class 구현 ]
더보기
[ + 코드 보기 ]
package org.zerock.myapp.service;
import java.util.List;
import java.util.Objects;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.zerock.myapp.domain.BoardVO;
import org.zerock.myapp.exception.DAOException;
import org.zerock.myapp.exception.ServiceException;
import org.zerock.myapp.mapper.BoardMapper;
import lombok.Setter;
import lombok.extern.log4j.Log4j2;
@Log4j2
@Service
public class BoardServiceImpl implements BoardService, InitializingBean, DisposableBean {
// + InitializingBean는 빈 객체가 만들어진 직후에, DisposableBean는 빈이 파괴되기 직전에
// ===========================================================
@Setter(onMethod_= {@Autowired})
private BoardMapper mapper;
// ===========================================================
// + 전체 게시글 조회
// ===========================================================
@Override
public List<BoardVO> getAllList() throws ServiceException {
// + 핵심 비지니스 로직 : DB 게시판 테이블을 조회하여, 게시글 전체목록을 얻어내 Model로 반환
// + 하지만, Model은 Controller에서만 Model을 생성할 수 있다.
// + 그러기 위해서는 mapper을 빈으로 가져와야 한다.
log.trace("getAllList() invoked.");
try {
Objects.requireNonNull(this.mapper);
return this.mapper.selectAllList();
} catch ( DAOException e ) {
throw new ServiceException(e);
} // try - catch
} // getAllList
// ===========================================================
// + 특정 게시글 조회
// ===========================================================
@Override
public BoardVO get(Integer bno) throws ServiceException {
log.trace("get() invoked.");
try {
return this.mapper.select(bno);
} catch (DAOException e) {
throw new ServiceException(e);
} // try - catch
} // get
// ===========================================================
// + 특정 게시글 삭제
// ===========================================================
@Override
public Integer remove(Integer bno) throws ServiceException {
log.trace("getAllList() invoked.");
try {
return this.mapper.delete(bno);
} catch (DAOException e) {
throw new ServiceException(e);
} // try - catch
} // remove
// ===========================================================
// + 게시물 추가 ( bno도 지정하는 버전 )
// ===========================================================
@Override
public Integer add(BoardVO vo) throws ServiceException {
log.trace("getAllList() invoked.");
try {
return this.mapper.insert(vo);
} catch (DAOException e) {
throw new ServiceException(e);
} // try - catch
} // add
// ===========================================================
// + 게시물 추가 2 ( bno를 시퀀스가 해주는 버전 )
// ===========================================================
@Override
public Integer addAuto(BoardVO vo) throws ServiceException {
log.trace("getAllList() invoked.");
try {
return this.mapper.insertSelectKey(vo);
} catch (DAOException e) {
throw new ServiceException(e);
} // try - catch
} // assAuto
// ===========================================================
// + 게시물 수정
// ===========================================================
@Override
public Integer update(BoardVO vo) throws ServiceException {
log.trace("update() invoked.");
try {
return this.mapper.update(vo);
} catch (DAOException e) {
throw new ServiceException(e);
} // try - catch
} // update
// ===========================================================
// + destroy : 후처리
// ===========================================================
@Override
public void destroy() throws Exception {
log.trace("destroy() invoked.");
} // DisposableBean : destroy ( 후처리 작업 )
// ===========================================================
// + afterPropertiesSet : 필드의 의존성 주입이 정삭적인지 체크
// ===========================================================
@Override
public void afterPropertiesSet() throws Exception {
log.trace("afterPropertiesSet() invoked.");
// + 의존성 주입 체크
Objects.requireNonNull(this.mapper);
log.trace("\t + this.mapper : {}", this.mapper);
} // InitializingBean : afterPropertiesSet ( 전처리 작업)
// ===========================================================
} // end class
[ 2 - 5. Service Test 하기 ]
더보기
[ + 코드 보기 ]
package org.zerock.myapp.service;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.Timeout;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.zerock.myapp.domain.BoardVO;
import org.zerock.myapp.exception.ServiceException;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.extern.log4j.Log4j2;
@Log4j2
@NoArgsConstructor
// JUNIT 5 버전
@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations = "file:src/main/webapp/**/spring/**/*-context.xml")
@TestInstance(Lifecycle.PER_CLASS)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class BoardServiceTests {
// =====================================================
@Setter(onMethod_= {@Autowired})
private BoardService service;
// =====================================================
@BeforeAll
void beforeAll() {
log.trace("beforeAll() invoked.");
Objects.requireNonNull(this.service);
log.trace("\t + this.service : {}", this.service);
} // beforeAll
// =====================================================
// + 1. 게시물 전체 조회
// =====================================================
@Disabled
@Test
@Order(1)
@DisplayName("1. test getAllList")
@Timeout(value = 3, unit = TimeUnit.SECONDS)
void testgetAllList() throws ServiceException {
log.trace("testgetAllList() invoked.");
this.service.getAllList().forEach(log::info);
} // testgetAllList
// =====================================================
// + 2. 특정 게시글 조회
// =====================================================
@Disabled
@Test
@Order(2)
@DisplayName("2. test Get")
@Timeout(value = 3, unit = TimeUnit.SECONDS)
void testGet() throws ServiceException {
log.trace("testGet() invoked.");
int bno = 77;
BoardVO vo = this.service.get(bno);
assertNotNull(vo);
log.info("\t + vo : {}", vo);
} // testGet
// =====================================================
// + 3. 특정 게시물 삭제
// =====================================================
@Disabled
@Test
@Order(3)
@DisplayName("3. test Remove")
@Timeout(value = 3, unit = TimeUnit.SECONDS)
void testRemove() throws ServiceException {
log.trace("testRemove() invoked.");
int bno = 7;
int affectedLine = this.service.remove(bno);
log.info("\t + affectedLine : {}", affectedLine);
} // testRemove
// =====================================================
// + 4. 게시글 추가 1
// =====================================================
@Disabled
@Test
@Order(4)
@DisplayName("4. test add")
@Timeout(value = 3, unit = TimeUnit.SECONDS)
void testAdd() throws ServiceException {
log.trace("testAdd() invoked.");
BoardVO vo = new BoardVO ( 678, "WOW", "WOWOWOW", "WOWW");
int affectedLine = this.service.add(vo);
log.info("\t + affectedLine : {}", affectedLine);
} // testAdd
// =====================================================
// + 5. 게시글 추가 2
// =====================================================
// @Disabled
@Test
@Order(5)
@DisplayName("5. test AddAuto")
@Timeout(value = 3, unit = TimeUnit.SECONDS)
void testAddAuto() throws ServiceException {
log.trace("testAddAuto() invoked.");
BoardVO vo = new BoardVO ( null, "AAAAAA", "BBBBB", "CCCC");
int affectedLine = this.service.addAuto(vo);
log.info("\t + affectedLine : {}", affectedLine);
} // testAddAuto
// =====================================================
// + 6. 게시글 수정
// =====================================================
// @Disabled
@Test
@Order(6)
@DisplayName("6. test Update")
@Timeout(value = 3, unit = TimeUnit.SECONDS)
void testUpdate() throws ServiceException {
log.trace("testUpdate() invoked.");
BoardVO vo = new BoardVO ( 10, "AAAAAA", "BBBBB", "CCCC");
int affectedLine = this.service.update(vo);
log.info("\t + affectedLine : {}", affectedLine);
} // testUpdate
} // end class
728x90