Spring의 Multi Datasource 관련해서 작성 했던
Spring Multi Datasource(다중 데이터소스)를 활용한 Service 구현 :: 신나게의 개발썰
Spring Multi Datasource(다중 데이터소스)를 활용한 Service 구현-2 :: 신나게의 개발썰
위 게시물들로 Multi Datasource는 마무리 짓으려고 합니다.
마무리 하기 전에 번외(?)편으로 Mybatis 관련 게시물을 작성하고 끝맺음으로 하려고 합니다.
Spring 개발자라면 Mybatis는 아무래도 빼놓을 수 없고 많이들 사용하는 기술이라고 생각합니다. 물론 요즘은 JPA와 같이 ORM 기술을 사용해서 데이터 절차 지향이 아닌 좀 더 순수한(?) 객제 지향 개발로 방향성을 잡고 있는것 또한 사실 입니다.
그러나 아직은 현역(이지만 사라지고 있을수도 있는..)이기 때문에 Mybatis 사용 시 간단한(?) 팁을 작성하려고 합니다.
다름이 아니고 @Mapper 어노테이션으로 선언된 Inteface 상속 입니다.
Mybatis도 Java의 인터페이스기반으로 동작하기 때문에 @Mapper
어노테이션으로 지정된 인터페이스를 상속하여 개발이 가능하기 때문에 상속의 이점을 @Mapper
에서도 얻을 수 있는 장점이 있습니다.
예제를 위한 상황은 저희들한테 가장 친숙한 게시판으로 하였습니다.
게시판은
자유 게시판
- Table : free_board
게임 게시판
- Table : game_board
게시판이 한 테이블이 아닌 다른 테이블로 나누어져 있기 때문에 게시물 유형과 관련 없이 전체 조회를 해야 하는 경우도 있어 게시물들을 관리하는 notice_board_control
이라는 테이블도 존재합니다.
@Mapper Interface
먼저 @Mapper
의 구조는 아래를 참고하시면 되겠습니다.
Write Mapper Interface
NoticeBoardWrite
인터페이스를 제외한 나머지 인터페이스에는 @Mapper
어노테이션이 선언된 인터페이스들입니다. 간략 설명으론
NoticeBoardWriteMapper
인터페이스에 게시물들을 관리하는 메서드들을 선언FreeBoardWriteMapper
,GameBoardWriteMapper
인터페이스들은NoticeBoardWriteMapper
를 상속하여 해당 기능들을 사용자유 게시판(
FreeBoardWriteMapper
), 게임 게시판(GameBoardWriteMapper
)은 게시물을 저장하는insertPosts
메서드만 구현
그리고 특이한 인터페이스가 있는데 FreeBoardControlWriteMapper
라는 인터페이스 입니다. 해당 인터페이스는 부모 인터페이스에 있는 기능을 재구현(@Override)해도 정상적으로 잘되는지 확인 목적 차 만들어진 인터페이스 입니다.
자유게시판인 경우 기본은 notice_board_control
테이블에 저장되지만 특정 상황(?)에선 free_board_control
테이블에 저장하기 위해 만들어진 인터페이스 입니다.
NoticeBoardWrite
package com.datasource.repo.core.noticeBoard.write;
import com.datasource.entity.noticeBoard.Posts;
/**
* <pre>
* 게시판 저장 인터페이스
* </pre>
*/
public interface NoticeBoardWrite {
/**
* <pre>
* 게시물 저장 메서드
* </pre>
*/
void insertPosts(Posts posts);
}
NoticeBoardWriteMapper
package com.datasource.repo.mybatis.write.noticeBoard;
import com.datasource.entity.noticeBoard.control.NoticeBoardControl;
import com.datasource.repo.core.noticeBoard.write.NoticeBoardWrite;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Options;
/**
* <pre>
* {@link NoticeBoardWrite} Mybatis용
* </pre>
*/
@Mapper
public interface NoticeBoardWriteMapper extends NoticeBoardWrite {
/**
* <pre>
* 게시물 유형 제어 정보 저장
* </pre>
*/
@Insert("INSERT INTO notice_board_control(reference_seq, notice_board_type)" +
" VALUES(#{referenceSeq}, #{noticeBoardType})")
@Options(useGeneratedKeys = true, keyProperty = "seq", keyColumn = "seq")
long insertPostsControl(NoticeBoardControl noticeBoardControl);
}
GameBoardWriteMapper
package com.datasource.repo.mybatis.write.noticeBoard;
import com.datasource.entity.noticeBoard.Posts;
import com.datasource.repo.core.noticeBoard.write.NoticeBoardWrite;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Options;
/**
* <pre>
* {@link NoticeBoardWriteMapper} Mybatis용 게임 게시판
* </pre>
*/
@Mapper
public interface GameBoardWriteMapper extends NoticeBoardWriteMapper {
/**
* <pre>
* {@link NoticeBoardWrite#insertPosts(Posts)} 게임 게시판 글 저장
* </pre>
*
*/
@Insert("INSERT INTO game_board(member_id, text) VALUES(#{memberId}, #{text})")
@Options(useGeneratedKeys = true, keyProperty = "seq", keyColumn = "seq")
@Override
void insertPosts(Posts posts);
}
FreeBoardWriteMapper
package com.datasource.repo.mybatis.write.noticeBoard;
import com.datasource.entity.noticeBoard.Posts;
import com.datasource.repo.core.noticeBoard.write.NoticeBoardWrite;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Options;
/**
* <pre>
* {@link NoticeBoardWriteMapper} Mybatis용 자유 게시판
* </pre>
*/
@Mapper
public interface FreeBoardWriteMapper extends NoticeBoardWriteMapper {
/**
* {@link NoticeBoardWrite#insertPosts(Posts)} 자유 게시판 글 저장
*/
@Insert("INSERT INTO free_board(member_id, text) VALUES(#{memberId}, #{text})")
@Options(useGeneratedKeys = true, keyProperty = "seq", keyColumn = "seq")
@Override
void insertPosts(Posts posts);
}
FreeBoardControlWriteMapper
package com.datasource.repo.mybatis.write.noticeBoard;
import com.datasource.entity.noticeBoard.control.NoticeBoardControl;
import com.datasource.repo.core.noticeBoard.write.NoticeBoardWrite;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Options;
/**
* <pre>
* {@link NoticeBoardWrite} Mybatis용 자유 게시판
* </pre>
*/
@Mapper
public interface FreeBoardControlWriteMapper extends FreeBoardWriteMapper {
/**
* <pre>
* 자유 게시판용 제어 정보 저장
* </pre>
*/
@Insert("INSERT INTO free_board_control(reference_seq, notice_board_type)" +
" VALUES(#{referenceSeq}, #{noticeBoardType})")
@Options(useGeneratedKeys = true, keyProperty = "seq", keyColumn = "seq")
@Override
long insertPostsControl(NoticeBoardControl noticeBoardControl);
}
Read Mapper Interface
Read인 경우 위 Write Mapper Interface 설명과 다를게 없습니다.
NoticeBoardReadMapper
인터페이스에서 게시물들을 관리하는 메서드들이 선언되어 있고 자유 게시판(FreeBoardReadMapper
), 게임 게시판(GameBoardReadMapper
)는 이를 상속받고 게시물을 조회하는 findPosts
메서드가 구현된 구조 입니다.
그리고 마찬가지로 FreeBoardControlReadMapper
인터페이스는 특정 상황에서 notice_board_control
테이블이 아닌 free_board_control
테이블 사용을 위해 만들어진 인터페이스 입니다.
NoticeBoardRead
package com.datasource.repo.core.noticeBoard.read;
import com.datasource.entity.noticeBoard.Posts;
/**
* <pre>
* 게시판 읽기 인터페이스
* </pre>
*/
public interface NoticeBoardRead {
/**
* <pre>
* 게시물 조회 메서드
* </pre>
*/
Posts findPosts(long seq);
}
NoticeBoardReadMapper
package com.datasource.repo.mybatis.read.noticeBoard;
import com.datasource.entity.noticeBoard.control.NoticeBoardControl;
import com.datasource.repo.core.noticeBoard.read.NoticeBoardRead;
import org.apache.ibatis.annotations.Arg;
import org.apache.ibatis.annotations.ConstructorArgs;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* <pre>
* {@link NoticeBoardRead} Mybatis용
* </pre>
*/
@Mapper
public interface NoticeBoardReadMapper extends NoticeBoardRead {
/**
* <pre>
* 게시물 유형 제어 정보 읽기
* Seq로 조회
* </pre>
*/
@ConstructorArgs({
@Arg(column = "reference_seq", javaType = Long.class)
, @Arg(column = "notice_board_type", javaType = String.class)
})
@Select("SELECT * FROM notice_board_control WHERE reference_seq = #{referenceSeq}")
NoticeBoardControl findNoticeBoardControl(long referenceSeq);
/**
* <pre>
* 게시물 유형 제어 정보 읽기
* Type으로 조회
* </pre>
*/
@ConstructorArgs({
@Arg(column = "reference_seq", javaType = Long.class)
, @Arg(column = "notice_board_type", javaType = String.class)
})
@Select("SELECT * FROM notice_board_control WHERE notice_board_type = #{noticeBoardType}")
List<NoticeBoardControl> findNoticeBoardTypeControl(String noticeBoardType);
}
GameBoardReadMapper
package com.datasource.repo.mybatis.read.noticeBoard;
import com.datasource.entity.noticeBoard.Posts;
import com.datasource.repo.core.noticeBoard.read.NoticeBoardRead;
import org.apache.ibatis.annotations.Arg;
import org.apache.ibatis.annotations.ConstructorArgs;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
/**
* <pre>
* {@link NoticeBoardReadMapper} Mybatis용 게임 게시판
* </pre>
*/
@Mapper
public interface GameBoardReadMapper extends NoticeBoardReadMapper {
/**
* <pre>
* {@link NoticeBoardRead#findPosts(long)} 게임 게시판 글 조회
* </pre>
*
*/
@ConstructorArgs({
@Arg(column = "member_id", javaType = String.class)
, @Arg(column = "text", javaType = String.class)
})
@Select("SELECT * FROM game_board WHERE seq = #{seq}")
@Override
Posts findPosts(long seq);
}
FreeBoardReadMapper
package com.datasource.repo.mybatis.read.noticeBoard;
import com.datasource.entity.noticeBoard.Posts;
import com.datasource.repo.core.noticeBoard.read.NoticeBoardRead;
import org.apache.ibatis.annotations.Arg;
import org.apache.ibatis.annotations.ConstructorArgs;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
/**
* <pre>
* {@link NoticeBoardReadMapper} Mybatis용 자유 게시판
* </pre>
*/
@Mapper
public interface FreeBoardReadMapper extends NoticeBoardReadMapper {
/**
* <pre>
* {@link NoticeBoardRead#findPosts(long)} 자유 게시판 글 조회
* </pre>
*
*/
@ConstructorArgs({
@Arg(column = "member_id", javaType = String.class)
, @Arg(column = "text", javaType = String.class)
})
@Select("SELECT * FROM free_board WHERE seq = #{seq}")
@Override
Posts findPosts(long seq);
}
FreeBoardControlReadMapper
package com.datasource.repo.mybatis.read.noticeBoard;
import com.datasource.entity.noticeBoard.control.NoticeBoardControl;
import org.apache.ibatis.annotations.Arg;
import org.apache.ibatis.annotations.ConstructorArgs;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* <pre>
* {@link NoticeBoardReadMapper} Mybatis용 자유 게시판
* </pre>
*/
@Mapper
public interface FreeBoardControlReadMapper extends FreeBoardReadMapper {
/**
* <pre>
* 자유 게시물용 유형 제어 정보 읽기
* Seq로 조회
* </pre>
*/
@ConstructorArgs({
@Arg(column = "reference_seq", javaType = Long.class)
, @Arg(column = "notice_board_type", javaType = String.class)
})
@Select("SELECT * FROM free_board_control WHERE reference_seq = #{referenceSeq}")
@Override
NoticeBoardControl findNoticeBoardControl(long referenceSeq);
/**
* <pre>
* 자유 게시물용 유형 제어 정보 읽기
* Type으로 조회
* </pre>
*/
@ConstructorArgs({
@Arg(column = "reference_seq", javaType = Long.class)
, @Arg(column = "notice_board_type", javaType = String.class)
})
@Select("SELECT * FROM free_board_control WHERE notice_board_type = #{noticeBoardType}")
@Override
List<NoticeBoardControl> findNoticeBoardTypeControl(String noticeBoardType);
}
Service
서비스 구현체들은 클래스가 많은 관계로 클래스 다이어그램으로 설명을 마치겠습니다. 소스코드는 맨 하단에 있는 Git주소에서 확인하실 수 있습니다.
Write
게임 게시판
자유 게시판
자유 게시판인 경우에는 앞서 설명했듯이 공용 게시물 관리 외 추가적으로 자유 게시판 전용 게시물 관리가 있기 때문에 이를 처리 할 수 있는 NoticeBoardMapperWriteService
인터페이스와 NoticeBoardMapperWriteServiceAbs
추상클래스가 더 존재 합니다.
해당 인터페이스와 추상클래스의 메서드는 매개변수로 NoticeBoardWriteMapper
인터페이스롤 받아 처리하게끔 되어 있습니다. 예제에 매개변수 인터페이스로 insertPostsControl
메서드를 재구현(@Override)한 FreeBoardControlWriteMapper
인터페이스를 사용합니다.
Read
게임 게시판
자유 게시판
Read 부분도 역시 자유 게시판 전용 게시물 관리 조회를 위한 NoticeBoardMapperReadService
인터페이스와 NoticeBoardMapperReadServiceAbs
추상 클래스가 존재합니다. 해당 인터페이스와 추상 클래스의 메서드들의 매개변수로 FreeBoardControlReadMapper
인터페이스를 사용합니다.
테스트
자유 게시판
자유 게시판 게시물 및 공용 게시물 관리 테이블 등록
@Test
void 자유게시판_조회() {
Posts insertPosts = new Posts("freeMemberId_1", "freeMemberId_1 이건 본문 내용 입니다.");
this.freeBoardWriteService.insertPosts(insertPosts);
Posts findPosts = this.freeBoardDecoratorReadService.findPosts(insertPosts.getSeq());
NoticeBoardControl findNoticeBoardControl = this.freeBoardDecoratorReadService.findNoticeBoardControl(findPosts.getSeq());
Assertions.assertTrue(findPosts.getSeq() > 0 && findNoticeBoardControl.getSeq() > 0);
}
자유 게시판 및 공용 게시물 관리 테이블에 등록 후 정상적으로 등록이 되었는지 조회까지 하는 테스트 코드 입니다. 결과로 아래를 확인하시면 정상적으로 free_board
,notice_board_control
테이블에 저장되고 조회 되는것을 확인하실 수 있습니다.
2. INSERT INTO free_board(member_id, text) VALUES('freeMemberId_1', 'freeMemberId_1 이건 본문 내용 입니다.')
{executed in 3 msec}
[INFO ] 2022-05-30 09:06:54.364 [Test worker] resultsettable(108) - |----|
[INFO ] 2022-05-30 09:06:54.365 [Test worker] resultsettable(108) - |seq |
[INFO ] 2022-05-30 09:06:54.365 [Test worker] resultsettable(108) - |----|
[INFO ] 2022-05-30 09:06:54.366 [Test worker] resultsettable(108) - |1 |
[INFO ] 2022-05-30 09:06:54.367 [Test worker] resultsettable(108) - |----|
[DEBUG] 2022-05-30 09:06:54.372 [Test worker] sqltiming(352) - org.apache.ibatis.executor.statement.PreparedStatementHandler.update(PreparedStatementHandler.java:47)
2. INSERT INTO notice_board_control(reference_seq, notice_board_type) VALUES(1, 'FB') {executed in 3 msec}
[INFO ] 2022-05-30 09:06:54.373 [Test worker] resultsettable(108) - |----|
[INFO ] 2022-05-30 09:06:54.373 [Test worker] resultsettable(108) - |seq |
[INFO ] 2022-05-30 09:06:54.374 [Test worker] resultsettable(108) - |----|
[INFO ] 2022-05-30 09:06:54.374 [Test worker] resultsettable(108) - |1 |
[INFO ] 2022-05-30 09:06:54.375 [Test worker] resultsettable(108) - |----|
[INFO ] 2022-05-30 09:06:54.391 [Test worker] ReplicationRoutingDataSource(13) - isCurrentTransactionReadOnly : slave
[INFO ] 2022-05-30 09:06:54.392 [Test worker] HikariDataSource(110) - pg-slave - Starting...
[INFO ] 2022-05-30 09:06:54.420 [Test worker] HikariDataSource(123) - pg-slave - Start completed.
[DEBUG] 2022-05-30 09:06:54.430 [Test worker] sqltiming(352) - org.apache.ibatis.executor.statement.PreparedStatementHandler.query(PreparedStatementHandler.java:64)
3. SELECT * FROM free_board WHERE seq = 1 {executed in 8 msec}
[INFO ] 2022-05-30 09:06:54.436 [Test worker] resultsettable(108) - |----|---------------|-----------------------------|---------------------------|
[INFO ] 2022-05-30 09:06:54.437 [Test worker] resultsettable(108) - |seq |member_id |text |registration_date |
[INFO ] 2022-05-30 09:06:54.437 [Test worker] resultsettable(108) - |----|---------------|-----------------------------|---------------------------|
[INFO ] 2022-05-30 09:06:54.438 [Test worker] resultsettable(108) - |1 |freeMemberId_1 |freeMemberId_1 이건 본문 내용 입니다. |2022-05-30 09:06:58.152584 |
[INFO ] 2022-05-30 09:06:54.439 [Test worker] resultsettable(108) - |----|---------------|-----------------------------|---------------------------|
[INFO ] 2022-05-30 09:06:54.443 [Test worker] ReplicationRoutingDataSource(13) - isCurrentTransactionReadOnly : slave
[DEBUG] 2022-05-30 09:06:54.447 [Test worker] sqltiming(352) - org.apache.ibatis.executor.statement.PreparedStatementHandler.query(PreparedStatementHandler.java:64)
4. SELECT * FROM notice_board_control WHERE reference_seq = 1 {executed in 2 msec}
[INFO ] 2022-05-30 09:06:54.450 [Test worker] resultsettable(108) - |----|--------------|------------------|---------------------------|
[INFO ] 2022-05-30 09:06:54.451 [Test worker] resultsettable(108) - |seq |reference_seq |notice_board_type |registration_date |
[INFO ] 2022-05-30 09:06:54.451 [Test worker] resultsettable(108) - |----|--------------|------------------|---------------------------|
[INFO ] 2022-05-30 09:06:54.452 [Test worker] resultsettable(108) - |1 |1 |FB |2022-05-30 09:06:58.152584 |
[INFO ] 2022-05-30 09:06:54.452 [Test worker] resultsettable(108) - |----|--------------|------------------|---------------------------|
[INFO ] 2022-05-30 09:06:54.481 [SpringApplicationShutdownHook] HikariDataSource(350) - pg-slave - Shutdown initiated...
[INFO ] 2022-05-30 09:06:54.484 [SpringApplicationShutdownHook] HikariDataSource(352) - pg-slave - Shutdown completed.
[INFO ] 2022-05-30 09:06:54.484 [SpringApplicationShutdownHook] HikariDataSource(350) - pg-master - Shutdown initiated...
[INFO ] 2022-05-30 09:06:54.488 [SpringApplicationShutdownHook] HikariDataSource(352) - pg-master - Shutdown completed.
자유 게시판 게시물 및 자유 게시물 관리 테이블 등록
@Test
void 자유게시판_전용_테이블_유형_조회() {
Posts insertPosts = new Posts("freeMemberId_1", "freeMemberId_1 이건 본문 내용 입니다.");
this.freeBoardWriteService.insertPosts(this.freeBoardControlWriteMapper, insertPosts);
Posts findPosts = this.freeBoardDecoratorReadService.findPosts(insertPosts.getSeq());
NoticeBoardControl findNoticeBoardControl = this.freeBoardDecoratorReadService.findNoticeBoardControl(this.freeBoardControlReadMapper, findPosts.getSeq());
Assertions.assertTrue(findPosts.getSeq() > 0 && findNoticeBoardControl.getSeq() > 0);
}
이번에는 자유 게시판 게시물이 공융 게시물 관리 테이블이 아닌 자유 게시판 전용 게시물 관리 테이블에 저장되는 케이스입니다. 결과로 아래를 확인하시면 정상적으로 free_board
, free_board_control
테이블에 등록 및 조회가 되는것을 확인하실 수 있습니다.
2. INSERT INTO free_board(member_id, text) VALUES('freeMemberId_1', 'freeMemberId_1 이건 본문 내용 입니다.')
{executed in 5 msec}
[INFO ] 2022-05-30 09:17:11.626 [Test worker] resultsettable(108) - |----|
[INFO ] 2022-05-30 09:17:11.628 [Test worker] resultsettable(108) - |seq |
[INFO ] 2022-05-30 09:17:11.628 [Test worker] resultsettable(108) - |----|
[INFO ] 2022-05-30 09:17:11.629 [Test worker] resultsettable(108) - |1 |
[INFO ] 2022-05-30 09:17:11.629 [Test worker] resultsettable(108) - |----|
[DEBUG] 2022-05-30 09:17:11.635 [Test worker] sqltiming(352) - org.apache.ibatis.executor.statement.PreparedStatementHandler.update(PreparedStatementHandler.java:47)
2. INSERT INTO free_board_control(reference_seq, notice_board_type) VALUES(1, 'FB') {executed in 2 msec}
[INFO ] 2022-05-30 09:17:11.636 [Test worker] resultsettable(108) - |----|
[INFO ] 2022-05-30 09:17:11.636 [Test worker] resultsettable(108) - |seq |
[INFO ] 2022-05-30 09:17:11.637 [Test worker] resultsettable(108) - |----|
[INFO ] 2022-05-30 09:17:11.638 [Test worker] resultsettable(108) - |1 |
[INFO ] 2022-05-30 09:17:11.638 [Test worker] resultsettable(108) - |----|
[INFO ] 2022-05-30 09:17:11.654 [Test worker] ReplicationRoutingDataSource(13) - isCurrentTransactionReadOnly : slave
[INFO ] 2022-05-30 09:17:11.655 [Test worker] HikariDataSource(110) - pg-slave - Starting...
[INFO ] 2022-05-30 09:17:11.670 [Test worker] HikariDataSource(123) - pg-slave - Start completed.
[DEBUG] 2022-05-30 09:17:11.674 [Test worker] sqltiming(352) - org.apache.ibatis.executor.statement.PreparedStatementHandler.query(PreparedStatementHandler.java:64)
3. SELECT * FROM free_board WHERE seq = 1 {executed in 2 msec}
[INFO ] 2022-05-30 09:17:11.679 [Test worker] resultsettable(108) - |----|---------------|-----------------------------|---------------------------|
[INFO ] 2022-05-30 09:17:11.680 [Test worker] resultsettable(108) - |seq |member_id |text |registration_date |
[INFO ] 2022-05-30 09:17:11.681 [Test worker] resultsettable(108) - |----|---------------|-----------------------------|---------------------------|
[INFO ] 2022-05-30 09:17:11.681 [Test worker] resultsettable(108) - |1 |freeMemberId_1 |freeMemberId_1 이건 본문 내용 입니다. |2022-05-30 09:17:15.414437 |
[INFO ] 2022-05-30 09:17:11.682 [Test worker] resultsettable(108) - |----|---------------|-----------------------------|---------------------------|
[INFO ] 2022-05-30 09:17:11.687 [Test worker] ReplicationRoutingDataSource(13) - isCurrentTransactionReadOnly : slave
[DEBUG] 2022-05-30 09:17:11.691 [Test worker] sqltiming(352) - org.apache.ibatis.executor.statement.PreparedStatementHandler.query(PreparedStatementHandler.java:64)
4. SELECT * FROM free_board_control WHERE reference_seq = 1 {executed in 2 msec}
[INFO ] 2022-05-30 09:17:11.692 [Test worker] resultsettable(108) - |----|--------------|------------------|---------------------------|
[INFO ] 2022-05-30 09:17:11.692 [Test worker] resultsettable(108) - |seq |reference_seq |notice_board_type |registration_date |
[INFO ] 2022-05-30 09:17:11.693 [Test worker] resultsettable(108) - |----|--------------|------------------|---------------------------|
[INFO ] 2022-05-30 09:17:11.693 [Test worker] resultsettable(108) - |1 |1 |FB |2022-05-30 09:17:15.414437 |
[INFO ] 2022-05-30 09:17:11.694 [Test worker] resultsettable(108) - |----|--------------|------------------|---------------------------|
[INFO ] 2022-05-30 09:17:11.717 [SpringApplicationShutdownHook] HikariDataSource(350) - pg-slave - Shutdown initiated...
[INFO ] 2022-05-30 09:17:11.718 [SpringApplicationShutdownHook] HikariDataSource(352) - pg-slave - Shutdown completed.
[INFO ] 2022-05-30 09:17:11.719 [SpringApplicationShutdownHook] HikariDataSource(350) - pg-master - Shutdown initiated...
[INFO ] 2022-05-30 09:17:11.722 [SpringApplicationShutdownHook] HikariDataSource(352) - pg-master - Shutdown completed.
게임 게시판
게임 게시판 게시물 및 공용 게시물 관리 테이블 등록
게임 게시판은 특정 상황(?)이 없는 관계로 공용 게시물 관리 테이블에만 등록 및 조회를 합니다.
@Test
void 게임게시판_조회() {
Posts insertPosts = new Posts("gameMemberId_1", "gameMemberId_1 이건 본문 내용 입니다.");
this.gameBoardWriteService.insertPosts(insertPosts);
Posts findPosts = this.gameBoardDecoratorReadService.findPosts(insertPosts.getSeq());
NoticeBoardControl findNoticeBoardControl = this.gameBoardDecoratorReadService.findNoticeBoardControl(findPosts.getSeq());
Assertions.assertTrue(findPosts.getSeq() > 0 && findNoticeBoardControl.getSeq() > 0);
}
게임 게시판도 마찬가지로 결과를 아래에서 확인하시면 정상적으로 game_board
,notice_board_control
테이블에 저장되고 조회 되는것을 확인하실 수 있습니다.
2. INSERT INTO game_board(member_id, text) VALUES('gameMemberId_1', 'gameMemberId_1 이건 본문 내용 입니다.')
{executed in 3 msec}
[INFO ] 2022-05-30 09:29:06.372 [Test worker] resultsettable(108) - |----|
[INFO ] 2022-05-30 09:29:06.372 [Test worker] resultsettable(108) - |seq |
[INFO ] 2022-05-30 09:29:06.373 [Test worker] resultsettable(108) - |----|
[INFO ] 2022-05-30 09:29:06.373 [Test worker] resultsettable(108) - |1 |
[INFO ] 2022-05-30 09:29:06.374 [Test worker] resultsettable(108) - |----|
[DEBUG] 2022-05-30 09:29:06.380 [Test worker] sqltiming(352) - org.apache.ibatis.executor.statement.PreparedStatementHandler.update(PreparedStatementHandler.java:47)
2. INSERT INTO notice_board_control(reference_seq, notice_board_type) VALUES(1, 'GB') {executed in 2 msec}
[INFO ] 2022-05-30 09:29:06.380 [Test worker] resultsettable(108) - |----|
[INFO ] 2022-05-30 09:29:06.380 [Test worker] resultsettable(108) - |seq |
[INFO ] 2022-05-30 09:29:06.381 [Test worker] resultsettable(108) - |----|
[INFO ] 2022-05-30 09:29:06.382 [Test worker] resultsettable(108) - |1 |
[INFO ] 2022-05-30 09:29:06.382 [Test worker] resultsettable(108) - |----|
[INFO ] 2022-05-30 09:29:07.405 [Test worker] ReplicationRoutingDataSource(13) - isCurrentTransactionReadOnly : slave
[INFO ] 2022-05-30 09:29:07.406 [Test worker] HikariDataSource(110) - pg-slave - Starting...
[INFO ] 2022-05-30 09:29:07.426 [Test worker] HikariDataSource(123) - pg-slave - Start completed.
[DEBUG] 2022-05-30 09:29:07.430 [Test worker] sqltiming(352) - org.apache.ibatis.executor.statement.PreparedStatementHandler.query(PreparedStatementHandler.java:64)
3. SELECT * FROM game_board WHERE seq = 1 {executed in 2 msec}
[INFO ] 2022-05-30 09:29:07.434 [Test worker] resultsettable(108) - |----|---------------|-----------------------------|---------------------------|
[INFO ] 2022-05-30 09:29:07.435 [Test worker] resultsettable(108) - |seq |member_id |text |registration_date |
[INFO ] 2022-05-30 09:29:07.435 [Test worker] resultsettable(108) - |----|---------------|-----------------------------|---------------------------|
[INFO ] 2022-05-30 09:29:07.436 [Test worker] resultsettable(108) - |1 |gameMemberId_1 |gameMemberId_1 이건 본문 내용 입니다. |2022-05-30 09:29:10.155827 |
[INFO ] 2022-05-30 09:29:07.436 [Test worker] resultsettable(108) - |----|---------------|-----------------------------|---------------------------|
[INFO ] 2022-05-30 09:29:07.440 [Test worker] ReplicationRoutingDataSource(13) - isCurrentTransactionReadOnly : slave
[DEBUG] 2022-05-30 09:29:07.443 [Test worker] sqltiming(352) - org.apache.ibatis.executor.statement.PreparedStatementHandler.query(PreparedStatementHandler.java:64)
4. SELECT * FROM notice_board_control WHERE reference_seq = 1 {executed in 2 msec}
[INFO ] 2022-05-30 09:29:07.444 [Test worker] resultsettable(108) - |----|--------------|------------------|---------------------------|
[INFO ] 2022-05-30 09:29:07.445 [Test worker] resultsettable(108) - |seq |reference_seq |notice_board_type |registration_date |
[INFO ] 2022-05-30 09:29:07.446 [Test worker] resultsettable(108) - |----|--------------|------------------|---------------------------|
[INFO ] 2022-05-30 09:29:07.447 [Test worker] resultsettable(108) - |1 |1 |GB |2022-05-30 09:29:10.155827 |
[INFO ] 2022-05-30 09:29:07.447 [Test worker] resultsettable(108) - |----|--------------|------------------|---------------------------|
[INFO ] 2022-05-30 09:29:07.473 [SpringApplicationShutdownHook] HikariDataSource(350) - pg-slave - Shutdown initiated...
[INFO ] 2022-05-30 09:29:07.475 [SpringApplicationShutdownHook] HikariDataSource(352) - pg-slave - Shutdown completed.
[INFO ] 2022-05-30 09:29:07.475 [SpringApplicationShutdownHook] HikariDataSource(350) - pg-master - Shutdown initiated...
[INFO ] 2022-05-30 09:29:07.479 [SpringApplicationShutdownHook] HikariDataSource(352) - pg-master - Shutdown completed.
마치며
이 글의 취지는 @Mapper
어노테이션이 선언된 인터페이스도 상속이 가능하기 때문에 단순히 DB에 접근해서 저장 및 조회하는 인터페이스라 해서 모든 메서드들을 한 곳에서 작성 하기 보다는 상속의 이점을 살려 적절한 재사용 및 책임을 분산하여 객체 지향의 이점을 취하는게 더 좋지 않을까 해서 작성하였습니다.
또한 불가피하게 MyBatis를 사용하되 Spring Data JPA의 CrudRepository 인터페이스를 사용한 구조 및 설계를 원하는 경우에는 우리들이 처해진 상황에 맞는 구조와 설계를 구상하고 만들 수도 있을것으로 보입니다.
예제 코드는 GitHub - sungwookkim/spring-multi-datasource-service at notice-board-mybatis에서 확인 하실 수 있습니다.
마지막으로 긴 글을 끝까지 읽어주신 분들에게 감사드리며 하시는 모든 개발이 빛을 볼 수 있길 기도드리겠습니다.
감사합니다.