새싹개발자 2020. 9. 24. 00:24

목표 : 스프링 프레임워크와 MyBatis를 연동해서 좀 더 빠르게 SQL을 처리할 수 있는 구조로 만든다.

 

MyBatis란?

- 자바의 관계형 데이터 베이스 프로그래밍을 보다 쉽게 도와주는 프레임워크

 

MyBatis를 왜 사용할까?

MyBatis는 흔히 SQL 매핑 프레임워크로 분류되는데, 개발자들은 JDBC 코드의 복잡하고 지루한 작업을 피하는 용도로 많이 사용한다. 즉, JDBC를 보다 편하게 사용하기 위해 개발되었다.

 

전통적인 JDBC와 MyBatis를 비교해보자.

전통적인 JDBC MyBatis
- 직접 Connection을 맺고 마지막에 close()
- PreparedStatement 직접 생성 및 처리
- PreparedStatement의 setXXX() 등에 대한 모든 작업을 개발자가 처리
- SELECT의 경우 직접 ResultSet 처리
- 자동으로 Connection close() 가능
- MyBatis 내부적으로 PreparedStatement 처리
- #{prop}와 같이 속성을 지정하면 내부적으로 자동 처리
- 리턴 타입을 지정하는 경우 자동으로 객체 생성 및 ResultSet 처리

- MyBatis는 기존의 SQL을 그대로 활용할 수 있고, 진입장벽이 낮은 편이어서 JDBC의 대안으로 많이 사용한다.

- 스프링 프레임워크의 특징 중 하나는 다른 프레임워크들과의 연동을 쉽게 하는 추가적인 라이브러리들이 많다는 것인데, MyBatis 역시 mybatis-spring이라는 라이브러리를 통해 쉽게 연동 작업을 처리할 수 있다.

 

<구조>

 

MyBatis 관련 라이브러리 추가

MyBatis와 mybatis-spring을 사용하기 위해서는 pom.xml 파일에 추가적인 라이브러리를 설정해야한다.

- spring-jdbc / spring-tx : 스프링에서 데이터베이스 처리와 트랜잭션 처리

(해당 라이브러리들은 MyBatis와 무관하게 보이지만 추가하지 않은 경우에 에러 발생하므로 주의한다.)

- mybatis / mybatis-spring : MyBatis와 스프링 연동용 라이브러리

 

 

SQLSessionFactory

- MyBatis에서 가장 핵심적인 객체 : SQLSession, SQLSessionFactory 

- SQLSessionFactory : 내부에서 SQLSession을 만들어내는 존재

개발에서는 SQLSession을 통해서 Connection을 생성하거나 원하는 SQL을 전달하고, 결과를 리턴 받는 구조로 작성.

 

스프링에 SQLSesseionFactory를 등록하는 작업을 한다.

-sqlSessionFactoryBean 이용

-> 패키지 명을 보면 MyBatis의 패키지가 아니라 스프링과 연동 작업을 처리하는 mybatis-spring 라이브러리의 클래스임을 알 수 있다.

 

테스트를 해보자.

테스트는 DataSourceTests.java 에 코드를 추가하여 진행한다.

(수정) import org.apache.ibatis.session.SqlSessionFactory를 추가하지 않았을 때 오류가 생겼다.

에러 내용 : type SqlSessionFactory cannot be resolved to a type

 

실행결과 ↓

 

스프링과의 연동처리

SQLSessionFactory를 이용해서 코드를 작성해도 직접 Connection을 얻어서 JDBC 코딩이 가능하다.

좀 더 편하게 작업하기 위해서는 SQL을 어떻게 처리할 것인지를 별도의 설정을 분리해 주고, 자동으로 처리되는 방식을 이용하는 것이 좋다.  이를 위해서는 MyBatis의 Mapper라는 존재를 작성해줘야 한다.

 

Mapper : SQL과 그에 대한 처리를 지정하는 역할을 한다.

MyBatis-Spring을 이용하는 경우 Mapper

1. 인터페이스 + 어노테이션

2. XML

 

Mapper 인터페이스

1. 인터페이스 + 어노테이션 방식

- org.zerock.mapper 패키지 생성, TimeMapper 인터페이스 추가

- Timemapper 인터페이스에  MyBatis 어노테이션을 이용해서 SQL 메소드를 추가

 

 

Mapper 설정

MyBatis가 동작할 때 Mapper를 인식할 수 있도록 root-context.xml에 추가적인 설정 필요

가장 간단한 방식 - <mybatis:scan> 태그 이용

 

- root-context.xml 파일 열고, Namespaces 항목에서 mybatis-spring 탭 선택

 

 

- root-context.xml 파일에 <mybatis-spring:scan> 태그 추가

 

<mybatis-spring:scan> 태그의 base-package 속성은 지정된 패키지의 모든 Mybatis 관련 어노테이션을 찾아서 처리.

- 자동으로 org.zercok.mapper 패키지를 인식하는 방식으로 작성

 

Mapper 테스트

Mybatis-Spring은 Mapper 인터페이스를 이용해서 실제 SQL 처리가 되는 클래스를 자동으로 생성

따라서 개발자들은 인터페이스와 SQL만을 작성하는 방식으로도 모든 JDBC 처리를 끝낼 수 있다.

 

- 작성한 TimeMapper를 테스트하는 코드는 아래와 같이 클래스를 생성하여 처리

 

 

- TimeMapperTests 클래스는 TimeMapper가 정상적으로 사용이 가능한지 알아보기 위한 테스트 코드이다.

- 위의 코드가 정상적으로 동작한다면 스프링 내부에는 TimeMapper 타입으로 만들어진 스프링 객체(빈)가 존재한다는 뜻

 

timeMapper.getClass().getName()

- 실제 동작한느 클래스의 이름을 확인해 준다.

- 실행 결과를 보면 개발 시 인터페이스만 만들어 주었는데 내부적으로 적당한 클래스가 만들어진 것을 확인 가능

 

실행결과

 

 

 

XML 매퍼와 같이 쓰기

SQL이 복잡하거나 길어지는 경우에는 어노테이션 보다는 XML 이용하는 방식을 더 선호하게 된다.

MyBatis-Spring 경우, Mapper 인터페이스와 XML을 동시에 이용 가능

 

XML을 작성해서 사용할 때,

1. XML 파일의 위치

2. XML 파일에 지정하는 namespace 속성

이 중요하다.

 

XML 파일 위치 : Mapper 인터페이스가 있는 곳에 같이 작성하거나, src/main/resources 구조에 XML 저장 폴더 생성

* XML 파일 이름 - Mapper 인터페이스와 같은 이름 사용하는 것이 가독성 good

 

 

- 아래와 같이 src/main/resources 폴더 내에 하위 폴더를 만들어 준다.

- 생성된 폴더에 TimeMapper.xml 파일 생성

 

 

 

- Mapper 인터페이스와 XML을 같이 이용해 보기 위해 기존의 TimeMapper 인터페이스(TimeMapper.java)에 추가적인 메소드 선언

 

TimeMapper 인터페이스를 보면 getTime2()가 추가된 것을 볼 수 있다. @Select와 같이 Mybatis 어노테이션이 존재하지 않고 SQL역시 존재하지 않는다.

 

 

- 실제 SQL은 XML을 이용해서 처리할 것이므로, 생성한 TimeMapper.xml은 다음과 같이 작성한다.

XML 매퍼를 이용할 때 주의할 부분

- <mapper>태그의 namespace 속성값

: Mybatis는 Mapper 인터페이스와 XML을 인터페이스 이름과 namespace 속성값을 가지고 판단,

위와 같이 org.zerock.mapper.TimeMapper 인터페이스가 존재

XML의 <mapper namespace="org.zerock.mapper.TimeMapper"> 와 같이 동일한 이름이 존재

-> 이를 병합해서 처리

따라서 위의 경우는 메소드 선언은 인터페이스에 존재하고 SQL에 대한 처리는 XML을 이용하는 방식

 

<select> 태그의 id 속성 값은 메소드의 이름과 동일하게 맞춰야 한다.

<select> 태그의 경우 resultType 속성을 가지는 데 이 값은 인터페이스에 선언된 메소드의 리턴 타입과 동일하게 작성

 

 

최종확인은 TimeMapperTests 클래스에서.

 

 

실행결과는 getTime()과 동일

 

 

 

log4jdbc-log4j2 설정

MyBatis는 내부적으로 JDBC의 PreparedStatement를 이용해서 SQL을 처리한다.

따라서 SQL에 전달되는 파라미터는 JDBC에서와 같이 '?'로 치환되어서 처리된다. 복잡한 SQL 경우 '?'로 나오는 값이 제대로 되었는지 확인하기가 쉽지 않고 실행된 SQL의 내용을 정확히 확인하기 어렵다.

SQL을 변환해서 PreparedStatement에 사용된 '?'가 어떤 값으로 처리되었는지 확인하는 기능을 추가하도록 한다.

SQL 로그를 제대로 보기 위해서는 log4jdbc-log4j2 라이브러리를 사용해야 한다.

 

- pom.xml에 라이브러리를 설정한다.

 

추가한 후,

1. 로그 설정 파일 추가

2. JDBC의 연결 정보 수정

 

 

우선, src/main/resources 밑에 log4jdbc.log4j2.properties 파일 추가

 

log4jdbc를 이용하는 경우, JDBC 드라이버와 URL 정보를 수정해야 한다.

root-context.xml 일부 수정

dataSource() 메소드에서 변경되는 부분은

JDBC 드라이버의 클래스를 'net.sf.log4jdbc.sql.jdbcapi.DriverSpy' 로 수정

JDBC 연결 URL 부분에서 중간에 'log4jdbc' 문자열 추가

 

테스트 코드 실행 결과 ↓

 

로그 레벨 설정

테스트 코드 실행 시, 많은 양의 로그가 출력되어서 불편하다.

테스트 코드가 실행될 때의 로그와 관련된 설정은 src/test/resources 밑의 log4j.xml 이용

 

 

 

테스트 코드가 실행될 때 보여지는 'INFO...' 메시지는 log4j.xml의 마지막 부분에 있는 설정에 영향을 받는다.

 

 

log4jdbc에서 출력되는 로그를 조절하고 싶다면 추가적인 <logger>를 조절해서 처리

기본 설정의 로그 info 레벨 -> warn 과 같은 더 높은 레벨의 로그로 수정

=> 테스트 코드를 실행할 때 이전에 비해 로그의 양 감소한다.

 

 

 

 

 

 

코드로 배우는 스프링 웹 프로젝트(개정판) - 구멍가게 코딩단