- 알아본 점
(1)MapperRegistry에 등록해야한다는 것.
(2)sqlSession 객체의 .getMapper 메소드로 인터페이스가 생성한 구현객체를 받는다.
(3)구현된 객체를 사용
(4)인터페이스의 구현객체의 메소드가 다수 값을 반환한다면 컬렉션 프레임웤을 이용한다.
Mapper Interface 부분
// MyBatis가 수행할 SQL 문장을 저장하는 자바 인터페이스 => "Mapper Interface" 라고 부른다!!
// MyBatis가 제공하는 Annotation의 속성에 SQL문장을 저장
// MyBatis가 구현객체를 자동으로 생성해준다.
public interface BoardMapper {
// @Param(바인드변수명) 형태로 사용해서, 바인드 변수에 값 전달
@Select("SELECT * FROM tbl_board WHERE bno > #{theBno} AND title LIKE '%'||#{search}||'%'")
public abstract List<BoardVO> selectAllBoards(@Param("theBno") Integer bno, @Param("search") String title);
// 마이바티스의 SQL 문장에서, 바인드 변수는 #{변수명} 형식으로 기재함
// 마이바티스는 처리할 SQL문장의 바인드 변수의 개수가 1개 뿐일 경우에는,
// 전달한 인자값을 무조건 바인드 값으로 binding 시킴
@Select("SELECT * FROM tbl_board WHERE bno = #{theBno}")
public abstract BoardVO selectBoard(Integer bno);
} // end interface
Test.java 부분
// @Disabled
@Test
@Order(2)
@DisplayName("2. selectAllBoards")
@Timeout(value=3, unit=TimeUnit.SECONDS)
void testSelectAllBoards() {
log.trace("testSelectAllBoards() invoked.");
@Cleanup
SqlSession sqlSession = this.sqlSessionFactory.openSession();
// 인터페이스가 구현한 구현객체를 달라
// MapperProxy@~~ 로 주소가 나온다. 마이바티스에서 구현객체를 만들어주는 대리인의 주소
// Dynamic Proxy객체 생성기법이라고 함
BoardMapper mapper = sqlSession.<BoardMapper>getMapper(BoardMapper.class);
// MapperRegistry에 등록해야한다. 설정 xml파일에 mappers > mapper 태그로 class 속성을 통해 경로를 적어준다.
assertNotNull(mapper);
log.info("\t+ mapper: {}", mapper);
List<BoardVO> list = mapper.selectAllBoards(200, "6");
Objects.requireNonNull(list);
list.forEach(log::info);
} // testSelectAllBoards
위에 부분까지는 기본적인 mybatis사용을 해봤다.
-알아본 점
<mappers>
<!-- 2. Mapper Interface 등록#1 : 각각의 Mapper Interface 등록방법 -->
<!-- <mapper class="org.zerock.myapp.mapper.BoardMapper"/> -->
<!-- 3. Mapper Interface 등록#2 : Mapper Interface 들이 모여있는 패키지단위로 등록하는 방법 -->
<package name="org.zerock.myapp.mapper" />
</mappers>
-알아본 점
// @Disabled
@Test
@Order(4)
@DisplayName("4. testGetCurrentTime1")
@Timeout(value=3, unit=TimeUnit.SECONDS)
void testGetCurrentTime1() {
log.trace("testGetCurrentTime1() invoked.");
SqlSession sqlSession = this.sqlSessionFactory.openSession();
try (sqlSession) {
// mybatis-config.xml 설정파일에 등록되지 "않은", Mapper Interface를 사용
Configuration config = sqlSession.getConfiguration();
Objects.requireNonNull(config);
log.info("\t+ 1. config: {}", config);
// 1. 설정파일에 등록되지 않은 Mapper Interface를 동적으로 설정에 추가
config.<TimeMapper>addMapper(TimeMapper.class);
// 2. 지정된 Mapper Interface를 구현한 MapperProxy 구현객체를 얻자!
TimeMapper mapper = sqlSession.<TimeMapper>getMapper(TimeMapper.class);
Objects.requireNonNull(mapper);
log.info("\t+ 2. mapper: {}", mapper);
// 3. Mapper Interface에 선언된 추상메소드 호출
String now = mapper.getCurrentTime1();
assert now != null;
log.info("\t+ 3. now: {}", now);
} // try-with-resources
} // testGetCurrentTime1
Interface 부분
// MyBatis's Mapper Interface #2
public interface TimeMapper {
@Select("SELECT to_char(current_date, 'yyyy/MM/dd HH24:mi:ss') AS now FROM dual")
public abstract String getCurrentTime1();
} // end interface
// 선처리작업(1회성)으로 마이바티스의 설정을 동적으로 생성(XML설정파일없이...)
@BeforeAll
void beforeAll() {
log.trace("beforeAll() invoked.");
// ------------------------------------
// Step1. Connection Pool을 제공하는 DataSource(javax.sql.DataSource 규격을 준수하는)를 생성
// ------------------------------------
HikariConfig hikariConfig = new HikariConfig();
// 1. 기본적인 JDBC Connection 생성을 위한 연결정보 4가지 설정
hikariConfig.setJdbcUrl("jdbc:log4jdbc:oracle:thin:@atp2201_high?TNS_ADMIN=C:/opt/OracleCloudWallet/ATP");
hikariConfig.setDriverClassName("net.sf.log4jdbc.sql.jdbcapi.DriverSpy");
hikariConfig.setUsername("ADMIN");
hikariConfig.setPassword("Oracle1278697");
// 2. Connection Pool과 관련된 설정
hikariConfig.setMaximumPoolSize(7);
hikariConfig.setMinimumIdle(3);
hikariConfig.setConnectionTimeout(1000 * 3);
hikariConfig.setConnectionTestQuery("SELECT 1 FROM dual");
// ------------------------------------
// Step2. Hikari DataSource 생성
// ------------------------------------
HikariDataSource dataSource = new HikariDataSource(hikariConfig); // 자원객체임
Objects.requireNonNull(dataSource);
// ------------------------------------
// Step3. TX 관리자 생성
// ------------------------------------
TransactionFactory txFactory = new JdbcTransactionFactory();
assertNotNull(txFactory);
// ------------------------------------
// Step4. 실행환경(Environment) 1개 생성
// ------------------------------------
Environment env = new Environment("development", txFactory, dataSource);
assertNotNull(env);
// ------------------------------------
// Step5. Configuration 생성
// ------------------------------------
Configuration conf = new Configuration(env);
assertNotNull(conf);
// ------------------------------------
// Step6. Configuration 에 Mapper Interface와 Mapper XML파일 등록
// ------------------------------------
conf.addMappers("org.zerock.myapp.mapper");
this.myBatisConfig = conf;
// ------------------------------------
// Step7. SqlSessionFactory 생성
// ------------------------------------
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
this.sqlSessionFactory = builder.build(myBatisConfig);
(1)Prepared SQL = SQL문장은 고정, 값만 변경해서 수행
(2)Dynamic SQL = SQL 문장이 비고정
이런식으로 동적 SQL을 따로 저장한다.
기본적으로 깔리는 문장
<?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="mappers.Board2Mapper">
<sql id="commonSQL">
SELECT *
FROM tbl_board
</sql>
-버젼에 따른 문법차이 = 부등호는 태그랑 겹쳐 낮은 버젼에서는 CDATA라는 선행조건이 있다
-OGNL이라는 표현식을 기반으로 작성 (자바 언어가 지원하는 범위보다 더 단순한 식을 사용)
<select
id="findBoardByBno"
resultType="org.zerock.myapp.domain.BoardVO">
SELECT *
FROM tbl_board
<!-- 검색조건1 : 특정 BNO에 해당하는 게시글 하나 조회 -->
<if test="bno != null">
WHERE bno = #{bno}
</if>
</select>
<select
id="findBoardsByWriter"
resultType="org.zerock.myapp.domain.BoardVO">
SELECT *
FROM tbl_board
<trim prefix="WHERE">
<if test="writer != null">
writer LIKE '%'||#{writer}||'%'
</if>
</trim>
</select>
<select
id="findBoardsByTitle"
resultType="org.zerock.myapp.domain.BoardVO">
SELECT *
FROM tbl_board
<!-- 검색조건2 : 특정 검색어를 포함하고 있는 모든 제목을 가지는 게시글 조회 -->
<where>
<if test="title != null">
title LIKE '%'||#{title}||'%'
</if>
</where>
</select>
<select
id="findBoardsByBnoOrTitle"
resultType="org.zerock.myapp.domain.BoardVO">
SELECT *
FROM tbl_board
<where>
<choose>
<when test="bno != null">
bno > #{bno}
</when>
<when test="title != null">
OR title LIKE '%'||#{title}||'%'
</when>
<otherwise>
bno > 0
</otherwise>
</choose>
</where>
</select>
<select
id="findBoardsByBnos"
resultType="boardVO">
<include refid="commonSQL" />
<!-- 여러개의 BNO와 일치하는 게시글 추출을 위한 조건식 구성 -->
<where>
<!-- bno IN ( 1, 2, 3, 4, 5 ) -->
bno IN
<foreach collection="list" open="(" close=")" index="idx" item="bno" separator=",">
#{bno}
</foreach>
</where>
</select>
TimeMapper.java
// MyBatis's Mapper Interface #2
public interface TimeMapper {
// ======================================================================= //
// 자동SQL문장실행방식으로 쿼리수행 : 단 이 방식은 몇가지 규칙을 지켜야 함:
// ======================================================================= //
// (1) 이 메소드가 수행할 SQL 문장을 Mapper XML 파일에 등록.
// (2) 위 (1) Mapper XML 파일의 위치는, 이 Mapper Interface의 패키지 구조 아래에 만들어야 함.
// (3) 위 (1) Mapper XML 파일의 이름은, 반드시 이 Mapper Interface의 이름과 동일하게 만들라!
// (4) 위 (3) 에서 만든 Mapper XML 파일의 `namespace` 속성의 값은, 반드시 이 Mapper Interface의 FQCN으로 지정하라!!
// (5) 자동실행시킬 SQL 문장을 가지고 있는 태그의 `id` 속성의 값은, 반드시 아래의 추상메소드의 이름과 동일하게
// 지정하라!!!
public abstract String getCurrentTime2();
} // end interface
TimeMapper.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">
<!-- Dynamic SQL 저장 -->
<mapper namespace="org.zerock.myapp.mapper.TimeMapper">
<select
id="getCurrentTime2"
resultType="java.lang.String">
SELECT to_char(current_date, 'yyyy/MM/dd HH24:mi:ss') AS now FROM dual
</select>
</mapper>
test.java
@Log4j2
@NoArgsConstructor
@TestInstance(Lifecycle.PER_CLASS)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class TimeMapperTests {
private SqlSessionFactory sqlSessionFactory;
@BeforeAll
void beforeAll() throws IOException { // 1회성 전처리 작업 수행
log.trace("beforeAll() invoked.");
// 마이바티스 설정파일에 대한 입력스트림 객체 생성
String path = "mybatis-config.xml";
InputStream is = Resources.getResourceAsStream(path);
// 단한번 SqlSessionFactory 공장객체를 생성해서, 필드에 저장
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
this.sqlSessionFactory = builder.build(is);
assertNotNull(this.sqlSessionFactory);
log.info("\t+ this.sqlSessionFactory: {}", this.sqlSessionFactory);
} // beforeAll
// @Disabled
@Test
@Order(1)
@DisplayName("1. testGetCurrentTime2")
@Timeout(value=10, unit=TimeUnit.SECONDS)
void testGetCurrentTime2() throws SQLException {
log.trace("testGetCurrentTime2() invoked.");
// @Cleanup("close")
SqlSession sqlSession = this.sqlSessionFactory.openSession();
try (sqlSession) {
TimeMapper mapper = sqlSession.<TimeMapper>getMapper(TimeMapper.class);
String now = mapper.getCurrentTime2();
assert now != null;
log.info("\t+ now: {}", now);
} // try-with-resources
} // testGetCurrentTime2
간단한 문장은 어노테이션을 이용하고
복잡한 문장은 Mapper.xml을 통해 설정하여 사용한다.