티스토리 뷰

 

[ 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
댓글
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
최근에 올라온 글
Total
Today
Yesterday
공지사항