90일차 Mybatis2

쿠우·2022년 8월 9일
0

-Mapper Interface를 이용하는 방법!

- 알아본 점
(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사용을 해봤다.

-디테일한 부분의 mybatis 사용법

1. 여러개의 매퍼 인터페이스를 패키지 채로 xml파일에 등록하는 방법

-알아본 점

  • 자동으로 스캐닝해서 해당 패키지의 인터페이스를 구현한다.
 	<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>

2. mybatis-config.xml 설정파일에 등록되지 "않은", Mapper Interface를 사용 하는 법 (order 4)

-알아본 점

  • config 객체를 반환한다.
    (config란 구성파일이라는 뜻 xml 설정이 되어있지 않는 인터페이스를 동적으로 추가해준다.)
  • sqlSession.getConfiguration() = sqlSession객체로 부터 구성파일 즉 config을 반환
  • config.addMapper(clazz) = 구성파일에 추가해준다.
  • 위에 추가 과정이 있었으니 나머지는 전에 배운 내용대로

//	@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

3. 모든 설정이 되어 있지 않을 때 ☆(길다)

  • HikariConfig 클래스를 사용
// 선처리작업(1회성)으로 마이바티스의 설정을 동적으로 생성(XML설정파일없이...)
	@BeforeAll
	void beforeAll() {
		log.trace("beforeAll() invoked.");
		
		// ------------------------------------
		// Step1. Connection Pool을 제공하는 DataSource(javax.sql.DataSource 규격을 준수하는)를 생성
		// ------------------------------------
		HikariConfig hikariConfig = new HikariConfig();
  • set~~~메소드를 사용해 environment안에 dataSource안에 property태그 세부속성 설정
  • .setConnectionTestQuery() 연결을 확인하는 메소드
		// 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");
  • HikariDataSource 클래스를 통하여 datasource를 생성
		// ------------------------------------
		// Step2. Hikari DataSource 생성
		// ------------------------------------
		HikariDataSource dataSource = new HikariDataSource(hikariConfig);	// 자원객체임
		
		Objects.requireNonNull(dataSource);
  • TransactionFactory 클래스는 sql 트랜잭션을 관리하는 클래스 / 트랜잭션 관리자 유형에 따라 자손클래스가 나뉜다.
		// ------------------------------------
		// Step3. TX 관리자 생성
		// ------------------------------------
		TransactionFactory txFactory = new JdbcTransactionFactory();
		assertNotNull(txFactory);
  • Enviroment 클래스로 실행환경 조성
		// ------------------------------------
		// Step4. 실행환경(Environment) 1개 생성
		// ------------------------------------
		Environment env = new Environment("development", txFactory, dataSource);
		assertNotNull(env);


  • Configuration 클래스룰 통해 xml파일과 동일한 환경을 조성한다.

		// ------------------------------------
		// Step5. Configuration 생성
		// ------------------------------------
		Configuration conf = new Configuration(env);
		assertNotNull(conf);
  • 위에 만들어진 객체의 addMappers 메소드를 이용해 xml파일에 패키지를 등록
		// ------------------------------------
		// Step6. Configuration 에 Mapper Interface와 Mapper XML파일 등록
		// ------------------------------------
		conf.addMappers("org.zerock.myapp.mapper");
				
		this.myBatisConfig = conf;
  • 위에 추가시켜서 완성된 구성파일을 이용하여 SqlsessionFactoryBuilder와 함께 sqlSessionFactory 객체에 정보 주입
		// ------------------------------------
		// Step7. SqlSessionFactory 생성
		// ------------------------------------
		SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
		this.sqlSessionFactory = builder.build(myBatisConfig);

4. 동적 SQL(마이바티스의 강력한 기능)

(1)Prepared SQL = SQL문장은 고정, 값만 변경해서 수행
(2)Dynamic SQL = SQL 문장이 비고정

이런식으로 동적 SQL을 따로 저장한다.

기본적으로 깔리는 문장

  • sql 태그는 반복되는 sql 구문에 대한 한 번의 정의로 재사용이 가능하도록 해준다. (include 태그로 사용)
<?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>

4-1마이바티스의 문법

-버젼에 따른 문법차이 = 부등호는 태그랑 겹쳐 낮은 버젼에서는 CDATA라는 선행조건이 있다
-OGNL이라는 표현식을 기반으로 작성 (자바 언어가 지원하는 범위보다 더 단순한 식을 사용)

  • if 는 test속성을 통해 조건을 넣고 조건에 따라 식을 생성해준다. 조건에 따라 사라지거나 생성
    <select
        id="findBoardByBno"
        resultType="org.zerock.myapp.domain.BoardVO">
        SELECT *
        FROM tbl_board

        <!-- 검색조건1 : 특정 BNO에 해당하는 게시글 하나 조회 -->        
        <if test="bno != null">
            WHERE bno = #{bno}
        </if>
    </select>
  • trim은 조건식이 여러개일때 AND나 OR로 연결을 결정하는 역할을 해줌
    <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>
  • where 는 다중 조건식에서 trim과 같은 효과이지만 복잡한 속성이 없는 where를 실무에서 더 많이씀
    <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>
  • choose 와 when / otherwise 는 자바의 switch 문의 case / default 와 같다.
    <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>
  • forEach는 자바의 for문과 같음 (어떤 형태로 저장할지 괄호, 구분자, 변수를 적는다.)
  • include 태그는 생성한 sql 태그를 삽입할 때 사용
    <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>

  • typeAlias 태그는 반복사용되는 공통된 결과 타입에 대해 mybatis-config.xml 설정 파일에서 정의하고 sql 매퍼 xml 파일에서 사용한다.

5. config.xml설정 파일은 순서에 맞게 설정 태그가 나와야한다.

6. myBatis의 자동 SQL문장실행 기능의 테스트:(짬뽕 사용법)

  1. 이 기능은 보통 스프링 프레임워크와 연동될 때 사용
  2. 하지만, 독립적인 마이바티스 프레임워크만 사용할 때에도, 이 기능이 작동할지 테스트
  • mapper설정하는 xml 파일의 명을 인터페이스 파일명과 맞춰주고 mapper태그의 name space속성을 사용하여 경로를 지정해주고 다이나믹sql 태그를 사용하며 id 속성에 메소드를 지정해준다.

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을 통해 설정하여 사용한다.

profile
일단 흐자

0개의 댓글