기초단계/SPRING

2023.01.25-1 Spring

춘핑이 2023. 1. 25. 20:38

Springboot

38. Mapper 객체 단위 Test with jUnit5와 XML에 resultMap 사용하기

매핑해두고나면 정상적으로 잘 생성되는지 단위기능 되는지 테스트 해보자. 그래서 이것이 단위테스트고 여기서 사용하는 프레임워크가 jUnit5이다.
마이바티스의 기능을 수행하는 것이다. 마이바티스의 starter가 있다면 사용할 수있다.
@MybatisTest만 붙이면 간단하게 시험해볼 수잇다.

http://mybatis.org/spring-boot-starter/mybatis-spring-boot-test-autoconfigure/
폼파일에 추가해주자.
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter-test</artifactId>
<version>2.1.3</version>
<scope>test</scope>
</dependency>

테스트하기 위한 클래스를 만들어야한다. 우리가 테스트하고 싶은것은 NoticeDao이기때문에 이 클래스를 오른쪽키 누르고 jUnit5 test파일 생성을 하면된다.


NoticeDaoTest클래스에서
우클릭 -> run as -> jUnitTest
메인함수를 만드는게 귀찮아서 사용해보는 것이다.

@AutoConfigureTestDatabase(replace = Replace.NONE)

테스트하기 위한 데이터소스와 실제 소스와 다를 수잇는데 따로 마련햇느냐? 물어봄 저걸작성하면 다른걸 안쓴다는 것이다.
그런데 잘 작성이 안된다. MVC다만들고 테스트하면 늦을 수잇으니 이렇게 하나씩 단위테스트를 해볼 필요가잇다.
MVC로작성하여서 통합테스트밖에 불가능하다면 junit을 사용하면 단위테스트를 사용할 수잇다.
그런데 만약 DB에는 MEMBER_ID이런식으로 구분을 햇다면 매핑을 어떻게 해야하는가. 어노테이션때는 설명을 햇다.

<resultMap type="com.newlecture.web.entity.NoticeView" id="noticeViewMap">
  <result property="memberName" column="member_name"/>
</resultMap>
<select id="getViewList" resultMap="noticeViewMap">

resultMap을만들어서 결과집합을 이름을 바꿔주는 것을 명시하고 select할때 타입을 가져오는 것이아니라 Map을가져오면 된다.

39. Dao 구현체 직접 구현하기 SqlSession

mapper로 만들엇는데 이번엔 직접 만들어보도록하자.
원래는 <mapper> 등 태그를읽어내고 SqlSessionFactiry 가 mapper container에 담앗다.
이것을 SqlSession을 사용해서 꺼내 읽어야한다. @Repsotiry 가 mapper를 읽어낸다. 스프링부트에서는 dao를생성안해도 @Mapper를해서 그냥 담기만 하면됫던것이다.

인터페이스를 구현하기 위해서 그냥 @Mapper를 사용햇다. @Mapper가 dao와 붙어잇다면 마이바티스와 강하게 결합되어잇는 느낌이 든다.
다양한 방법으로 dao를 구현할 수잇는데 mapper를 지우고싶다.
@mapper가 IOC에 담아주는데 이것을 지우면 컨트롤러에서 사용할 수 없게된다. 그래서 직접 구현해보자.

@Repository
public class MybatisNoticeDao implements NoticeDao NoticeDao를 구현하는 것을 해보자.

@Component 중 @Controller @Service @Repository 의 3번째를 사용하는 것이다.
Mapper객체를 얻어낼땐 SqlSession을 얻어내서 할 수잇다. Dao에서는 이게 준비되어야하고 마이바티스는 라이브러리에서 IOC컨테이너에 담겨잇다.
sqlSession.getMapper(NoticeDao.class); NoticeDao의 클래스 정보를 제공하고 이것의 구현체를 꺼내달라 하는 것임.
Dao를 직접 구현햇을때 장점이 무엇인가? 완전히 분리함으로써 mybaits가 아니어도 jpa등 다른 것을 사용할수도 잇다.
구현체를 직접 구현할 수 잇다면 구현하는게 낫다.
마이바티스는 오버로드를 지원하지 않아서 인터페이스를 마이바티스에 맞춰 하나만 할 수잇다.

@Repository
public class MybatisNoticeDao implements NoticeDao{

@Autowired
private SqlSession sqlSession;

@Override
public List<NoticeView> getViewList(int offset, int size, String field, String query, boolean pub) {
    NoticeDao noticeDao = sqlSession.getMapper(NoticeDao.class);

    return noticeDao.getViewList(offset, size, field, query, pub);
}

40. 나머지 Dao 구현체 직접 구현하기 Constructor Injection

sqlSession으로 매퍼를 얻어서 하는것이 문제가 발생할 수잇다. 코드가 계속 같기때문임. 좀 줄일수잇는방법이 없을까?
반복을 줄이기 위해서 생성자에 넣어서 실행되도록하자.
NoticeDao의 추상메소드를 사용 -> 구현체인 MybatisNoticeDao가 생성됨 -> 생성하면서 mapper 필드를 초기화함 -> 리턴문의 mapper가 구현됨

private NoticeDao mapper;

@Autowired 
    public MybatisNoticeDao(SqlSession sqlSession) {
        mapper = sqlSession.getMapper(NoticeDao.class);
    }

@Override
public int getCount(String field, String query, boolean pub) {

    return mapper.getCount(field, query, pub);
}

@Override
public NoticeView getView(int id) {
    // TODO Auto-generated method stub
    return mapper.getView(id);
}

@Override
public Notice getNext(int id) {
    // TODO Auto-generated method stub
    return mapper.getNext(id);
}

@Override
public Notice getPrev(int id) {
    // TODO Auto-generated method stub
    return mapper.getPrev(id);
}

@Override
public int update(Notice notice) {
    // TODO Auto-generated method stub
    return mapper.update(notice);
}

@Override
public int insert(Notice notice) {
    // TODO Auto-generated method stub
    return mapper.insert(notice);
}

@Override
public int delete(int id) {
    // TODO Auto-generated method stub
    return mapper.delete(id);
}

@Override
public int deleteAll(int[] ids) {
    // TODO Auto-generated method stub
    return mapper.deleteAll(ids);
}

@Override
public int updatePubAll(int[] ids, boolean pub) {
    // TODO Auto-generated method stub
    return mapper.updatePubAll(ids, pub);
}

41.Service 계층 구현하기

직접 구현한 dao를 넣어보자. pub같은 것이 추가되어서 고쳐야할 것이 생겻다.

@Override
public int updatePubAll(int[] pubIds, int[] closeIds) {
    int result = 0;
    result += noticeDao.updatePubAll(pubIds, true);
    result += noticeDao.updatePubAll(pubIds, false);

    return result;
}

int updatePubAll(int[] ids, boolean pub);을 두번실행해서 결과에 더해주고 출력하자.
void test() {
System.out.println(service.getCount());
}
를 해보면 에러가 발생한다. 통합테스트를 하면 뭐가 문제인지 알수없다.
dao에서도 하면 또 문제가 발생하는것을 보면 dao에서 문제가 잇다는 것을 알 수잇다.
mapper를 보면 select문이 잘못되엇다는 것을 알수잇다.

<select id="getCount">
  SELECT count(id) count FROM notice
  where ${field} like '%${query}%' 
</select>

반환타입이 int인데 반환타입을 적어주지 않았다. resultType="int"를 넣어주자

잘됫다라고 넘어가면안된다. pub를 추가으니 count메소드에소드의 인자에도 boolean pub를 넣어주면서 고쳐야할 부분들을 모두 고쳐야한다.
mapper도 변경해준다.