일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |
- 국비지원
- TodayILearned
- 자바스크립트
- sqldeveloper
- 프레임워크
- 스프링
- maven
- 오라클
- javaprogramming
- JavaScript 내장객체
- sql
- SpringMVC
- springaop
- CSS
- HTML
- web
- 자바프로그래밍
- framework
- 서브쿼리
- PWA
- Oracle
- 프로그레시브웹앱
- javascript
- TIL
- 메이븐
- mybatis
- tdd
- js
- 생활코딩
- progressive web app
- Today
- Total
1cm
자바 프로그래밍_Day_94_Framework:MyBatis/TDD 본문
2021. 12. 30
수업을 한 3번 돌려보고 쓰는 TIL이다. 3번 돌려봐도 이해가 안되는게 문제이면서 내 스스로가 넘 답답하다^.^..
MVC2 수업 들을때와 같은 멘붕.. 어렵다 어려워!
MyBatis와 TDD.. 정말 친해지고 싶다.
이 날 배운 Mybatis는 영속성 framework중에 하나인데, 영속성이랑 데이터가 생성된 프로그램이 종료가 되어도 데이터가 사라지지 않고 유지되는 속성을 얘기한다. 기존에 영속성을 유지하려면 자바같은 경우 파일로 만들어서 영속성을 유지하거나 DB에 데이터를 저장해서 영속성을 구현했는데 Mybatis는 쿼리문을 따로 파일에 저장해놓고 가져다 쓰기 때문에 쿼리문을 수행할 때 반복적으로 사용하는 코드들을 줄여주는 프레임워크다.
Mybatis 란?
데이터의 입력, 조회, 수정, 삭제(CRUD)를 보다 편하게 하기 위해 xml로 구조화한 Mapper 설정 파일을 통해서 JDBC를 구현한 영속성 프레임 워크이다. 기존에 JDBC를 통해 구현했던 상당 부분의 코드와 파라미터 설정(?로 되어있는 부분) 및 결과 매핑을 xml 설정을 통해 쉽게 구현할 수 있게 해준다.
> Mybatis API 사이트
MyBatis – 마이바티스 3 | 소개
Copyright © 2009–2021MyBatis.org. .
mybatis.org
Mybatis의 흐름
이전에 JDBC Template를 통해 SQL을 실행했었다면 Mybatis는 해당 흐름을 전용 라이브러리를 통해 대체하여 동작한다고 생각하면 된다. 고로 DAO에서 Mybatis 라이브러리를 사용하는 구조인거다.
Mybatis의 동작 구조
- mybatis-config.xml : Class의 Alias(별칭) 설정 / DB연결 설정 / Sql 구문 경로 설정 - mybatis에 대한 환경설정 파일이라고 보면 된다.
- mapper.xml : SQL 쿼리문 설정(인자값, 결과값, 데이터 타입등 설정) / 각 패키지마다 존재(ex. 게시판, 멤버 패키지 등) - 실제로 실행시킬 쿼리문들을 가지고 잇는 파일(설정)이다.
- Build method 호출 시 session factory가 생성이 된다.
- Session : SQL session -> JDBC의 connection 객체라고 보면 된다.
Mybatis 구조가 헷갈려서 구글링으로 그나마 내가 이해할 수 있었던 게시글을 찾아봤다. 역시 그림 설명이 나에겐 훨씬 쉽게 다가온다^.ㅠ..
Mybatis 구조
유저의 첫번째 요청이 들어오기전에 톰캣은 SqlSessionFactory를 미리 준비 시켜 놓는다. 팩토리를 짓기 위해서 팩토리의 설계도가 필요할것이다 그것이 바로 MyBatis Config File이다. 그리고 나서 유저
simsimjae.tistory.com
ibatis와 Mybatis
기존에 Apache project에서 ibatis를 운영하던 팀이 2010년 5월 9일에 Google 팀으로 이동하면서 Mybatis로 이름을 바꾸게 되었다. Mybatis는 기존의 ibatis의 한계점이었던 동적 쿼리와 어노테이션 처리를 보강하여 더 나은 기능을 제공하는데, ibatis는 현재 비활성화 상태이며 기존에 ibatis로 만들어진 애플리케이션의 지원을 위해 라이브러리만을 제공하고 있다. -> ibatis는 mybatis의 옛 버전이라고 생각하면된다.
ibatis와 Mybatis의 차이점
ibatis | Mybatis | ||
Java 요구 버전 | JDK 1.4 이상 사용 가능 | JDK 1.5 이상 사용 가능 | |
패키지 내부 구조 변경 | com.ibatis.* | org.apache.ibatis.* | |
사용 용어의 변경 | rootElement | sqlMap | mapper |
namespace | 생략가능 | 필수 | |
parameter | parameterclass | parameterType | |
result | resultclass | resultType | |
변수입력 | #param# | #{param} | |
동적 쿼리 지원 | X | if, choose, trim, foreach 문 지원 | |
자바 어노테이션 지원 | X | O |
mybatis-config 설정
수업에서는 maven을 사용하고 있어서 mvnrepository에서 MyBatis를 검색 -> 코드 복사 후 pom.xml <dependencies>안에 추가해줬다.
그리고 main폴더 밑에 resources폴더를 만들어줬다.
resources 폴더 내에는 프로젝트에서 필요한 자원들(설정파일 등)을 넣어주는 폴더가 된다.
resources 폴더 밑에 mybatis-config.xml 파일을 생성해줬다.
mybatis-config.xml을 생성하면 target 폴더에 자동으로 업데이트 되어 생성되는 것을 확인할 수 있다.
그 다음 mybatis 웹 사이트 -> 시작하기 -> 설치 -> 스크롤 내리다 보면 Mybatis 설정 파일임을 선언해주는 코드를 복사해서 mybatis-config.xml 파일에 붙여 넣어준다.
- environments 를 구분해주는 값은 id이며, 여러개의 데이터베이스를 접근하고자 할 때에는 여러개를 같이 넣어주면 된다. 그 중 기본으로 연결할 데이터베이스는 default에 작성한다.
POOLED와 UNPOOLED의 차이점
구분 | POOLED | UNPOOLED |
특징 | 최초 Connection 객체를 생성할 때 해당 정보를 pool 영역에 저장해두고 이후 Connection 객체를 생성할 때 이를 재 사용함 | Connection 객체를 별도로 저장하지 않고, 객체 호출 시 매번 생성하여 사용 |
장점 | Connection 객체를 생성하여 DataBase와 연결을 구축하는데 걸리는 시간이 단축됨 | Connection 연결이 많지 않은 코드를 작성할때 간단하게 구현 가능 |
단점 | 단순한 로직을 수행하는 객체를 만들기에는 설정해야 할 정보가 많음 | 매번 새로운 Connection 객체를 생성하므로 속도가 상대적으로 느림 |
- 요청이 많은 서비스에 대해서는 미리 connection 객체를 만들어주는 것이 더 효율적이다.
mapper 설정하기
main/resources/mappers/member/member-mapper.xml 생성 후 target폴더 밑에도 자동으로 생성되었는지 확인
mapper 설정하기 - 참고
속성명 | 내용 |
id | 구문을 찾기 위해 사용될 수 있는 네임스페이스 내 유일한 구분자 |
parameterType | 구문에 전달될 파라미터의 클래스 명(패키지 경로 포함)이나 별칭 |
resultType | 리턴되는 타입의 패키지 경로를 포함한 전체 클래스 명이나 별칭, collection인 경우 list, arraylist로 설정 가능 |
resultMap | 사용할 resultMap의 id를 기술 |
flushCache | 이 값을 true로 설정하면 구문이 호출될 때마다 로컬, 2nd 레벨 캐시가 지워짐(flush). - 기본 값 : false |
useCache | 이 값을 true로 설정하면 구문의 결과가 2nd 레벨 캐시에 저장됨 - 기본 값 : true |
timeout | 예외가 발생하기 전에 데이터베이스의 요청 결과를 기다리는 최대시간 설정. (드라이버에 따라 다소 지원되지 않을 수 있음) |
statementType | STATEMENT, PREPARED 또는 CALLABLE 중 하나를 선택할 수 있다. Mybatis에게 Statement, PreparedStatement 또는 CallableStatement를 사용하게 한다. - 기본값 : PREPARED |
* resultMap과 resultType은 둘 모두를 사용할 수 없으며, 둘 중 하나만 선언 가능하다.
FlushCache / useCache
일반적으로 쿼리를 수행할 때 쿼리의 결과나 호출되는 내용이 변동이 없는 정적인 쿼리나 결과라면, 이를 매 반복 시마다 굳이 새로운 쿼리로 생성하여 호출하거나, 새로운 결과를 받아 올 필요가 없을 것임. 이러한 상황을 위해 Mybatis에서는 Cache라는 저장소를 내장하여 반복되는 정적인 쿼리의 호출이나 결과에 대한 내용을 한 번 이상 실행할 경우 이를 미리 저장해두어 재호출에 소요되는 시간을 절약할 수 있게 도와줌.
Mapper 설정하기 - 참고 : <insert>, <update>, <delete> 태그 주요 속성
속성명 | 내용 |
id | 구문을 찾기 위해 사용될 수 있는 네임스페이스 내 유일한 구분자 |
parameterType | 구문에 전달될 파라미터의 클래스 명(패키지 경로 포함)이나 별칭 |
flushCache | 이 값을 true로 설정하면 구문이 호출될 때마다 캐시가 지워짐(flush). - 기본 값 : false |
timeout | 예외가 발생하기 전에 데이터베이스의 요청 결과를 기다리는 최대시간 설정. (드라이버에 따라 다소 지원되지 않을 수 있음) |
userGeneratedKeys | (insert, update 에만 적용) 데이터베이스에서 내부적으로 생성한 키(ex. MySQL 또는 SQL Server의 자동 증가 필드)를 받는 JDBC getGeneratedKeys 메소드를 사용하도록 설정 - 기본 값 : false |
keyProperty | (insert, update 에만 적용) getGeneratedKeys 메소드나 insert 구문의 selectKey 태그의 설정 select문의 결과를 저장할 property를 지정. 디폴트는 세팅하지 않음. 여러 개의 컬럼을 사용한다면 프로퍼티명에 콤마(,)를 구분자로 나열 가능. |
Mybatis 활용하기 - SqlSession
SqlSession 생성하기 -> Template 클래스 생성 : mybatis-config.xml, *-mapper.xml 파일 생성을 완료했다면, common 패키지를 만들어 싱글톤을 적용한 Template 클래스를 만들고 SqlSession을 반환해주는 static 메소드를 작성한다.
만드는 순서는 아래와 같다.
1. mybatis-config.xml의 설정 정보를 InputStream 객체를 통해 읽어옴
2. SqlSessionFactoryBuilder 객체를 생성 하고 build() 메소드를 통해 SqlSessionFactory 객체를 생성
3. SqlSessionFactory 객체의 openSession() 메소드를 통해 SqlSession 객체 생성
SqlSession 생성하기 - 참고
- SqlSessionFactoryBuilder 메소드
메소드 | 설명 |
build(InputStream) | config.xml 파일만 불러옴 |
build(InputStream, String) | config.xml 파일과 지정한 DB를 불러옴 |
build(InputStream, Properties) | config.xml 파일과 프로퍼티로 설정한 내용으로 불러옴(" ${ key명 } ") |
build(InputStream, String, Properties) | config.xml 파일과 지정한 DB, Properties 파일을 불러옴 |
build(configuration) | configuration 객체에 설정한 내용을 불러옴 |
* config.xml은 Resource 객체의 getResouceAsStream 메소드를 이요하요 InputStream으로 가져옴
- SqlSessionFactory 메소드
메소드 | 설명 |
openSession() | 기본값을 통해 SqlSession을 생성한다. |
openSession(Boolean) | SqlSession 생성 시 AutoCommit 여부를 true / false로 지정할 수 있다. - 기본 값 : true |
openSession(Connection) | 직접 생성한 Connection 객체를 이용해 SqlSession을 생성한다. - 기본 값 : X |
openSession(ExecutorType) | 쿼리를 실행할 때 PreparedStatement의 재사용 여부를 설정한다. - 기본 값 : ExecutorType.SIMPLE |
SqlSession을 통한 쿼리 실행
- Service 클래스에서 getSqlSession 메소드 호출을 통해 SqlSession 생성
- Dao 클래스의 메소드 호출 시 전달 인자로 SqlSession 객체 전달
- Dao 클래스의 메소드에서 SqlSession 객체를 통해 쿼리에 접근
메소드 | 반환형 | 설명 |
selectOne(String mapper, Object param) | Object | 하나의 객체만을 받고자 할 때 사용 |
selectList(String mapper, Object param) | List<E> | 결과에 대한 값을 List로 받고자 할 때 사용 |
selectMap(String mapper, Object param, String mapKey) | Map<K,V> | 결과에 대한 값을 Map으로 받고자 할 때 사용 (마지막 인자로 키로 사용될 컬럼을 명시) |
insert(String mapper, Object param) | int | DB에 데이터를 입력하고자 할 때 사용 |
update(String mapper, Object param) | int | DB의 데이터를 수정하고자 할 때 사용 |
delete(String mapper, Object param) | int | DB의 데이터를 삭제하고자 할 때 사용 |
TDD(Test-Driven-Development)란?
- 개발 방법론 중 하나이며, 설계 -> 테스트코드 -> 구현 코드 작성의 순서로 진행되는데, 테스트가 주도하는 개발이라고 보면 된다.
- 수업에서는 member에 접근해서 데이터 조회 하는 것들은 TDD 방식으로 진행했다.
실습 코드
> mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- Mybatis 설정 파일임을 선언 -->
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- Mybatis 설정과 관련된 기본 설정들을 선언하는 영역 -->
<configuration>
<!--
Database에 연결할 설정 정보들을 선언하는 영역
default 속성
Database에 연결 설정을 여러 개 생성할 수 있는데 그 중에서 기본으로 연결할 설정 정보를 지정하는 속성이다.
-->
<environments default="web">
<environment id="web">
<!--
type 속성
JDBC : JDBC가 commit, rollback을 직접 사용 가능하도록 하는 옵션
MANAGED : JDBC가 트랜잭션에 대해 어떠한 영향도 행사하지 않는 옵션 -> JDBC에게 트랜잭션을 managed 할 수 있게 역할 위임
-->
<transactionManager type="JDBC" />
<!--
실제 DB 접속에 관한 정보를 넣는 태그
type 속성
POOLED : 커넥션 객체를 미리 만들어서 POOL에 저장해서 사용하는 설정, 객체를 만드는 시간을 줄일 수 있다.
UNPOOLED : DB 요청이 올 때마다 커넥션 객체를 만들고 사용이 끝나면 닫는 설정,
커넥션 객체를 미리 만들어놓는 설정이 아니므로 상대적으로 느릴 수 있다.
-->
<dataSource type="POOLED">
<property name="driver" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" />
<property name="username" value="WEB" />
<property name="password" value="WEB" />
</dataSource>
</environment>
<environment id="kh">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" />
<property name="username" value="WEB" />
<property name="password" value="WEB" />
</dataSource>
</environment>
<!--
<environment id="student">
</environment>
-->
</environments>
<!-- DB에서 사용하는 쿼리문들을 담는 mapper 파일을 등록하는 영역 -->
<mappers>
<mapper resource="mappers/member/member-mapper.xml"/>
</mappers>
</configuration>
> main/java/com/kh/mybatis/app.java
package com.kh.mybatis;
import org.apache.ibatis.session.SqlSession;
import com.kh.mybatis.common.SqlSessionTemplate;
/**
* Hello world!
*
*/
public class App {
public static void main( String[] args ) {
System.out.println( "Hello World!" );
SqlSession session = SqlSessionTemplate.getSession();
System.out.println(session);
}
}
> main/java/com/kh/mybatis/common/SqlSessionTemplate.java
package com.kh.mybatis.common;
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class SqlSessionTemplate {
public static SqlSession getSession() {
InputStream is = null;
SqlSessionFactoryBuilder builder = null;
SqlSessionFactory factory = null;
SqlSession session = null;
try {
// Resources는 Mybatis에서 제공하는 유틸 클래스로
// 클래스 패스로부터 자원(Resource)를 쉽게 읽어오는 메소드를 제공한다.
is = Resources.getResourceAsStream("mybatis-config.xml");
builder = new SqlSessionFactoryBuilder();
factory = builder.build(is);
// factory = builder.build(is, "kh");
// true : 오토커밋 활성, false : 오토커밋 비활성
session = factory.openSession(false);
} catch (IOException e) {
e.printStackTrace();
}
return session;
}
}
> main/java/com/kh/mybatis/member/model/dao/MemberDao.java
package com.kh.mybatis.member.model.dao;
import org.apache.ibatis.session.SqlSession;
public class MemberDao {
public int getMemberCount(SqlSession session) {
return session.selectOne("memberMapper.selectCount");
}
}
> main/java/com/kh/mybatis/member/model/service/MemberService.java
package com.kh.mybatis.member.model.service;
import org.apache.ibatis.session.SqlSession;
import com.kh.mybatis.common.SqlSessionTemplate;
import com.kh.mybatis.member.model.dao.MemberDao;
public class MemberService {
private MemberDao dao = new MemberDao();
// 데이터베이스에서 숫자를 조회 후 리턴해주는 역할
public int getMemberCount() {
int count = 0;
SqlSession session = SqlSessionTemplate.getSession();
count = dao.getMemberCount(session);
session.close();
return 2;
}
}
- 클래스 생성 후 ctrl + 1 눌러서 TestCase 생성
> main/resources/mappers/member/member-mapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- Mapper 설정 파일임을 선언 -->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace 속성 : 외부에서 mapper에 접근할 수 있는 이름(별칭) -->
<mapper namespace="memberMapper">
<!--
SELECT 구문
id 속성 : 쿼리문의 고유 아이디
resultType 속성 : 쿼리 실행 후 조회 된 결과값의 자료형
-->
<select id="selectCount" resultType="_int">
SELECT COUNT(*) FROM MEMBER
</select>
</mapper>
> src/test/java/com/kh/mybatis/AppTest.java
package com.kh.mybatis;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import com.kh.mybatis.common.SqlSessionTemplate;
@DisplayName("첫 번째 테스트 코드 작성")
public class AppTest {
private SqlSession session = null;
// 테스트 메소드들이 실행되기 전에 실행되는 메소드 (가장 먼저 딱 한 번만 실행된다.)
@BeforeAll
public static void init() {
System.out.println("@BeforeAll");
}
// 각각의 테스트 메소드들이 실행되기 전에 무조건 실행되는 메소드
// @BeforeEach
public void setup() {
// @BeforeEach확인용 - Disabled 유무로 메소드 역할 확인 가능
System.out.println("@BeforeEach");
session = SqlSessionTemplate.getSession();
}
@Test
// @Disabled // 테스트 클래스 또는 메소드를 비활성화 할 수 있다.
public void nothing() {
// 이 테스트 메소드를 통해서 현재 프로젝트가 테스트가 가능한 환경인지 확인한다.
}
@Test
@DisplayName("SqlSession 생성 테스트")
public void create() {
// assertNotNull(SqlSessionTemplate.getSession());
assertNotNull(session);
}
}
- AppTest 클래스는 Test 클래스이며, 어노테이션 @Test로 되어있는 부분들은 Test Method이다.
- @DisplayName은 클래스, 메소드 둘 다 적용이 가능하다.
- Assertions : 클래스 단정 메소드들을 가지고 있고, assert라는 매소드들의 테스트 수행 결과를 판별하는 메소드들을 가지고 있는 클래스다. (말이 어렵네..)
> src/test/java/com/kh/mybatis/member/model/service/MemberServiceTest.java
package com.kh.mybatis.member.model.service;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@DisplayName("Member 테스트")
class MemberServiceTest {
private MemberService service;
@BeforeEach
public void setUp() {
service = new MemberService();
}
@Test
@Disabled
public void nothing() {
}
@Test
@Disabled
public void create() {
assertThat(service).isNotNull();
}
@Test
public void getMemberCountTest() {
int count = service.getMemberCount();
// assertThat(count).isGreaterThanOrEqualTo(2);
assertThat(count).isPositive().isGreaterThanOrEqualTo(2);
}
}
- test 진행 시 Docker 켜놓고 테스트 진행하는 거 잊지 않기!
수업 끝
'국비지원_Java > Java Programming_2' 카테고리의 다른 글
자바 프로그래밍_Day_98_Framework : MyBatis 동적 쿼리 / 게시글 조회 (0) | 2022.01.12 |
---|---|
자바 프로그래밍_Day_97_Framework : MyBatis 동적 SQL (0) | 2022.01.08 |
자바 프로그래밍_Day_96_Framework : MyBatis / 동적 쿼리 (0) | 2022.01.06 |
자바 프로그래밍_Day_95_Framework:MyBatis (0) | 2022.01.04 |
자바 프로그래밍_Day_93_Framework:Maven (0) | 2021.12.29 |