본문 바로가기
Spring 프레임워크/메모

[ Spring ] MyBatis 연결 하기 01

by Hwanii_ 2023. 9. 27.
728x90

개요 

지난 프로젝트 에서,
처음에는 JDBC 인터페이스의 preparedStatement 를 사용한 방식의 DAO 였고,
두번째는 JdbcTemplate 템플릿 패턴을 활용한 방식의 DAO 였다.
 
SpringBoot 에서 MyBatis 프레임워크 적용 방법을 공부했기에, 프로젝트에 이를 적용해 보기로 했다.
 
이 게시글은 SQLSessionTemplate 템플릿 패턴을 활용한 방식으로 MyBatis 와의 연결 하는 방식 이다.
 
 
 

설정 방법

1.
maven 설정 파일을 사용하는 프로젝트 여서, pom.xml 에 MyBatis 프레임워크 관련 라이브러리를 추가.
 

 
본인은 각각 3.5.13 버전과 2.0.7 버전을 사용 했다.
 
아래의 더보기 에서 다양한 버전을 확인 할 수 있다. 본인에 환경에 맞는 버전을 적용 하면 된다.
 

더보기

 

 

 
2.
스프링 프레임워크의 본인이 지정한 루트 컨테이너에 DataSource 빈을 등록 한다.
 
본인은 루트 컨테이너의 이름을 applicationContext.xml 이라고 했다.
 

 
DataSource 빈의 id 속성값은 dataSource 라고 지정해 줬고,
 
<property> 태그를 사용하여 세터 주입 방식으로 데이터베이스 연결 정보를 설정 한다.
 
참고
 

더보기

 

나의 프로젝트 DBMS는 오라클이고, 19버전 이상 부터는 1521:orcl 으로 해야 한다.

 

오라클 11버전은 1521:xe 으로 설정 하면 되고,

 

그외에 설정 및 다른 DBMS 을 사용 해야 한다면 구글 검색을 통해 해결 하자.

 
3.
루트 컨테이너에 SQLSession 빈을 등록 한다.
 
SQLSession 빈을 루트 컨테이너에 등록 해야 하는 이유 ?
 

더보기

 

MyBatis를 Spring 프레임워크 에서 사용 할 때,

SQLSessionTemplate 템플릿 패턴을 활용한 연결 방식 으로 구현 하는 경우,

SQLSession 빈을 등록 하지 않는다 ?

>>

MyBatis를 아예 사용 (연결) 할 수가 없다.

SQLSession은 MyBatis의 핵심 인터페이스 이기 때문에 반드시 빈 등록을 설정 해야 한다.

 

1)

스프링 트랜잭션 관리 :

SQLSession은 MyBatis의 핵심 객체로, 데이터베이스와의 상호작용을 담당한다.

 

스프링은 트랜잭션 관리를 위해 사용 할 수 있는 여러 데이터 소스를 지원 하기에,

SQLSession 빈을 스프링 빈으로 등록하면 스프링의 트랜잭션 관리와 연동 하기가 용이해진다.

 

2)

라이프사이클 관리 :

스프링 컨테이너는 빈의 라이프사이클을 관리 할 수 있는 기능을 가진다.

따라서, SQLSession 빈을 루트 컨테이너에 등록하는 순간, 해당 빈의 생성 및 소멸을 관리 할 수 있게 된다.

이를 통해서, 메모리 누수 또는 자원 누수를 방지 할 수 있게 된다.

 

3)

설정 및 관리의 편의성 :

스프링 빈으로 SQLSession을 등록하면 MyBatis의 설정 파일과 연결하여,

스프링 프레임워크와 함께,

데이터베이스 연결 정보 및 트랜잭션 관리 및 캐시 설정 등을 효과적으로 관리 할 수 있게 된다.

 

정리하자면,

스프링과 MyBatis를 함께 사용 할 때, 안정적인 코드 구현 및 관리의 편리성 향상의 효과를 얻을 수 있다.

 

 
sqlSession 빈은 class 속성값에 적혀있는 것을 보면 알 수 있듯이,
팩토리 패턴으로 생성되는 빈 인것을 확인 할 수 있다.
 
그리고, 세터 주입 방식으로 위에서 정의한 DataSource 빈의 id 속성값을 참조해주면 된다.
 
configLocation 은 MyBatis 설정 파일의 경로를 지정하는 것인데,
SQLSession 빈이 해당 설정 파일을 사용 할 수 있도록 설정하는 방식 이다.
 
설정 파일의 이름은 mybatis-config.xml 이라고 지정해 주었고,
해당 설정 파일의 위치는 루트컨테이너가 있는 위치와 동일 하게 하면 된다.
 

 
참고
 

더보기

 

위와 같이 하는 방식은 해당 설정 파일에서

데이터베이스의 연결 정보,

SQL 매퍼 파일의 위치,

설정 및 기타 MyBatis 관련 설정,

..

등을

설정 하여 관리 할 수 있게 된다.

 
참고 2
 

더보기

 

자동 검색 (Mapper Scanning) 방식

 

( 자동 검색을 통해 런타임에 매퍼를 찾아내기 때문에 유연한 방식 이라고 한다.. )

 

 

ConfigLocation 속성이 아닌, mapperLocation 속성을 사용 해서,

매퍼 XML 파일이 위치한 경로 패턴을 지정 할 수 있다.

 

본인 기준으로, src/main/resource 경로 하위에 mybatis 라는 폴더 (패키지) 하위에 매퍼 XML 파일이 있기 때문에,

위와 같이 경로 패턴을 지정해 줬다.

 

매퍼 XML 경로 패턴은 아래와 같은 경로 으로도 사용 할 수 있다.

 

 
4.
루트 컨테이너에 SQLSessionTemplate 빈을 등록 한다.
 
SQLSessionTemplate 빈을 등록 해야 하는 이유 ?
 

더보기

 

SQLSessionTemplate 은 SQLSession을 더 편리하게 사용 할 수 있도록 도와주는 스프링의 템플릿 클래스 이다.

 

SQLSessionTemplate 을 스프링 빈으로 등록하면 SQLSession 을 Wrapping 하고,

MyBatis의 SQLSession 과 관련된 작업을 훨씬 간편하게 수행 할 수 있도록 도와준다.

트랜잭션 관리,

예외 처리,

스레드 안정성

을 처리하는 데 도움을 준다.

 

정리하자면,

 

1)

SQLSessionTemplate 은 일반적인 MyBatis 예외를 스프링의 DataAccessException 예외로 변환 해준다.

따라서,

MyBatis의 예외를 직접 처리 하기 보다는,

스프링의 DataAccessException 예외를 처리하는게 더 일반적이기도 하고 편리 하다고 할 수 있다.

 

2)

SQLSessionTemplate 은 다양한 MyBatis 작업을 수행 하기 위한 메서드를 제공 한다.

코드를 훨씬 간결 하게 작성 할 수 있도록 도와주기 때문에, 편의성 및 확장성이 증가 하게 된다.

 

 

 

만약 SqlSessionTemplate 을 사용 하지 않는 다면 ?

 

<bean class="org.apache.ibatis.session.SqlSession">
    <constructor-arg ref="sqlSession"/>
</bean>

 

위와 같이 class 속성값을 부여하고, sqlSession을 빈을 참조 하면 된다.

 

 
빈의 class 속성값을 보고 알 수 있듯이 템플릿 디자인 패턴을 기반으로 동작하는 스프링의 클래스 이다.
 
템플릿 패턴 이란 ?
 

더보기

 

템플릿 디자인 패턴은 소프트웨어 개발에서 반복되는 알고리즘의 일부분을 추상화 하고,

구체적인 단계를 하위의 클래스에게 위임하여,

일반화된 알고리즘 구조를 정의하는 디자인 패턴 이다.

 

1. 알고리즘의 일반화 :

템플릿 패턴은 알고리즘의 공통 부분을 추상화 하고, 일반화 할 수 있게 한다.

>>

코드의 중복을 피하고, 일관성 유지

 

2. 유지보수용이한 코드 및 높은 확장성 :

템플릿 패턴을 사용하면 알고리즘의 구조를 한 곳에서 정의 하므로,

변경이 필요한 경우 해당 구조만 수정 하면 된다.

>>

유지보수용이 및 높은 확장성

 

3. 확장 가능한 프레임워크 구축 :

템플릿 패턴은 프레임워크 또는 라이브러리를 구축 할 때 매우 유용 하다.

 

4. 코드 재사용성 증가 :

템플릿 패턴은 일반화된 알고리즘 구조를 제공하기 때문에, 다양한 상황에서 동일한 패턴을 재사용 할 수 있다.

 

5. 디자인 패턴 준수 :

템플릿 패턴은 객체 지향 디자인 원칙을 따르기 때문에,

객체 지향 소프트웨어의 구조를 따르며,  품질을 향상 시킬 수 있게 된다. 

 
SqlSessionTemplate 는 세터 주입 방식이 아닌, 생성자 주입 방식 이다.
 
위에서 등록해줬던, SQLSession 빈의 id 속성값을 참조해주면 된다.
 
5.
매퍼 XML 파일을 생성 및 내용 작성 하기.
 

 
본인은 src/main/resources/mybatis/ 경로에 사용할 매퍼 XML 파일을 생성 했다.
 
게시글 (리뷰) 에 관련된 테이블이라 BoardMapper 라고 이름을 지어 주었다.
 
매퍼 XML 파일 에는 아래와 같이 스키마와 CRUD 관련 태그를 작성 하면 된다.
 

<?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="BoardMapper">

    <insert id="">
        ..
    </insert>
    
    <select id="">
    	..
    </select>
    
    <select id="">
        ..
    </select>
    
    <update id="">
    	..
    </update>
    
    <delete id="">
    	..
    </delete>
    
</mapper

 
중요한 부분은 <mapper> 태그 내부에 namespace 속성 이다.
 
SqlSessionTemplate 는 MyBatis의 SQLSession을 래핑한 스프링의 템플릿 클래스 라고 했었다.
그래서,
SqlSessionTemplate 을 사용하려면 SQL 쿼리문을 실행 할 때,
매퍼 XML 파일의 경로를 직접 지정해줘야 한다.
이때,
MyBatis의 매퍼 XML 파일에 지정된 namespace 속성값과 반드시 일치 해야 한다.
 
이렇게 하는 이유는 MyBatis가 쿼리를 찾아서 실행할 때,
해당 매퍼의 네임스페이스를 인식하고 실행 하기 때문 이다.
 
뿐만아니라,
C R R U D 에 해당하는 태그의 id 속성값은 DAO 클래스의 C R R U D 메서드명과 반드시 같아야 한다.
 
매퍼 XML 파일 예시 코드
 

 
6.
SqlSessionTemplate 을 사용할 DAO 클래스 생성 하기.
 

 
BoardDAO3 클래스에 @Repository 어노테이션을 달아, 메모리에 올라 갈 수 있도록 하고,
 
@Autowired 어노테이션을 달아, SqlSessionTemplate 객체를 의존 주입 한다.
 
5번 에서 정리했듯이,
SqlSessionTemplate의 메서드의 인자로,
MyBatis의 매퍼 XML 파일에 지정된 namespace 속성값과 반드시 일치 해야 한다고 했다.
 
SqlsessionTemplate 는 다양한 메서드를 가지고 있다.
 

 
7.
mybatis-config.xml 설정 하기
 
5에서 생성했었던, BoardMapper.xml 이라는 이름의 매퍼 XML 파일이 위치한 경로를 설정 한다.
 
이 설정을 함으로써, sqlSession 빈이 매퍼 XML 파일이 위치한 경로를 찾을 수 있게 된다.
 

 
위의 설정을 모두 잘 따라왔으면, Spring 프레임워크와 MyBatis 프레임워크의 연결이 정상적으로 수행 된다.
 
 
 
 
 
 
 
 
 
 

MyBatis 프레임워크를 적용하면서 발생한 이슈 정리

 
SpringBoot 에서는 gradle 설정 파일을 사용한 방식 이였는데,
현재 프로젝트 에서는 maven 설정 파일을 사용한 방식이여서, 큰 차이가 없을 것이라고 생각 했다.
 
하지만,
생각과는 다르게 설정에 차이가 있었는데, 정확한 이유는 잘 모르겠다.
SpringBoot와 Spring 의 차이인지 ?
또는,
SpringBoot 에서는 스타터 프로젝트 였는데,
Spring 에서는 레거시 프로젝트 여서 인지 ? 
 
 
 

첫번째 이슈
 

pom.xml 에 추가하는 MyBatis 관련 .jar 라이브러리 파일의 버전의 호환성 문제가 발생 했다.
>>
java.lang.UnsupportedClassVersionError 
 

더보기

 

프로젝트의 자바 버전과 MyBatis 라이브러리 버전의 호환성 문제 인지 ?

또는

스프링 프로젝트의 버전과 MyBatis 라이브러리 버전의 호환성 문제 인지 ?

또는

프로젝트 자바 버전 + 스프링 프로젝트 버전 + MyBatis 라이브러리 버전 모두의 호환성 문제인지 ?

 

정확히 모르겠다.

 

우선 본인이 사용 하고 있는 자바 버전은 11.0.20 버전이고,

프로젝트의 자바 버전은 자바 1.6 버전 이였다.

facet 에서 자바 버전을 11 버전으로 업그레이드 하더라도,

maven 설정 파일을 새로고침 하면 다시 1.6 버전으로 돌아왔다.

 

 

 

 

위와 같이 자바 버전을 1.6 (6) 에서 11 로 변경 하면,

 

 

JRE 도 JavaSE-11 로 변경 되는 모습을 확인 할 수 있었지만,

 

 

maven 설정 파일을 새로고침 하고,

 

 

OK 를 누르면, 

 

 

 

다시 1.6 (6) 버전으로 돌아와 있는 .. (왜 이러는건지 모르겠다 ..)

 

++ 23.09.28 

 

 

pom.xml 에서 메이븐 컴파일러에 자바 버전이 1.6 으로 되어 있어서 발생한 이슈 였다.

 

위의 이미지와 같이

<source>11</source>

<target>11</target>

 

으로 변경 해주니, 자바 11 버전으로 컴파일을 하게 되었다.

 

해결 방법 :
 

1)
프로젝트의 자바 JDK 버전을 최신 버전으로 새로 생성 한다.
 
2)
MyBatis 관련 라이브러리 버전을 프로젝트의 자바 JDK 버전을 호환하는 버전으로 다운그레이드 한다.
 
2)의 방법으로 발생한 이슈를 해결 할 수 있었다.
 

    <!-- MyBatis -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.13</version>
    </dependency>

    <!-- MyBatis Spring (MyBatis <-> Spring 연동) -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>2.0.7</version>
    </dependency>

 
수정한 라이브러리의 버전은 위와 같다.
 
위의 버전으로 수정 후 해결 할 수 있었다.
 
버전을 너무 올리거나, 내리면, 서버가 켜지지 않는 문제가 발생 한다.
 
 
 

두번째 이슈
 

SpringBoot 에서는 @Mapper 어노테이션을 사용하는 방법으로 MyBatis 설정을 했었다.
그래서 프로젝트 에서도 DAO 관련 인터페이스를 생성하고,
해당 인터페이스 내부에 @Mapper 어노테이션을 사용 하는 상황 이였다.
 
그런데,
@Mapper 어노테이션이 있음에도 불구하고 해당 인터페이스가 메모리에 올라 가지 않아서,
이 인터페이스를 사용하는 주체가 되는 서비스 레이어 내부에,
@Autowired 어노테이션으로 의존 주입 설정을 해두었지만,
BeanCreationException 이 발생 했다.
 

해결 방법 :

 
src/main/resources 내부에 있는 루트 컨테이너로 설정한 applicationContext.xml 설정 파일에서,
 

<mybatis-spring:scan base-package="com.spring.biz"/>

 
위의 코드를 추가 해서 인터페이스를 읽을 수 있게 됬다.
 
위의 코드는 MyBatis 에서 매퍼 인터페이스를 스캔 하고, 빈으로 등록하는 기능을 가진다.
이 코드를 작성 하면 MyBatis 에서 데이터베이스 작업을 수행 하는 매퍼 인터페이스를 빈으로 등록 할 수 있게 된다.
 
본인이 설정한 MyBatis는 board (리뷰) 테이블에 관련된 파일 이였다.
 
그래서 board 테이블 관련 파일이 모여있는 패키지 내부에 mapper (인터페이스) 를 생성 했고,
 
해당 패키지의 상위 패키지 경로로 잡아 주었다.
 
 
 

세번째 이슈

 
사실 세번째 이슈는 위의 두번째 이슈의 연장선 이였다.
 
org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'boardService' for bean class [com.spring.biz.board.BoardService] conflicts with existing, non-compatible bean definition of same name and class [com.spring.biz.board.BoardServiceImple]
 
라는 org.springframework.context.annotation.ConflictingBeanDefinitionException 에러가 발생 했었는데,
 
발생 이유로는 스프링 빈 객체화 설정의 이름 중복으로 인한 충돌 현상 이였다.
 
루트 컨테이너 에서,
@Component, @Repository, @Service, @Controller 어노테이션 설정이 달려 있는
여러 파일들을 우선적으로 메모리에 올리기 위해서 작성 했었던 코드가 있었다.
 

<context:component-scan base-package="com.spring.biz"/>

 
바로 위의 코드 였다.
 
본인은 com.spring.biz 라는 패키지 내부에 여러 테이블을 생성하는 구조 였기에, 위와 같이 작성 했었다.
 
그런데, 
 
인터페이스 == mapper 스캔을 위한 코드 였던,
 

<mybatis-spring:scan base-package="com.spring.biz"/>

 
두 구성 요소가 같은 패키지를 스캔 하거나 또는 동일한 빈을 중복으로 정의 하도록 설정 되면
충돌 문제가 발생 할 수도 있다고 한다.

 
해결 방법 :

 
인터페이스 == mapper 파일을 모아놓은 별도의 패키지를 생성 해서 경로를 새롭게 잡아 해결 할 수 있었다.
 

<mybatis-spring:scan base-package="com.spring.mapper"/>

 
com.spring 하위에 mapper 라는 이름의 패키지를 생성 하고,
mapper 패키지 내부에 mapper 인터페이스를 모아놓는 설정으로 변경 하여 해결 완료.
 
 
 

네번째 이슈

 
사실상 이 이슈로 인해서 두번째 이슈 + 세번째 이슈를 해결 했던것이 모두 의미가 없어지게 되었다.
 
Request processing failed; nested exception is org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.spring.mapper.BoardMapper.selectAll
 
라는 BindingException 에러가 발생 했다.
 
인텔리제이 IDE 에서는,
두번째 이슈와 세번째 이슈를 해결 하니, 프로젝트가 정상적으로 작동되는 모습을 확인 할 수 있었지만,
 
이클립스 또는 STS 에서는 두번째 이슈와 세번째 이슈를 해결하더라도,
BindingException 이 발생 하는 문제가 발생 했다.
 
인텔리제이 에서는 매퍼 인터페이스의 이름과 매퍼 xml 파일의 이름을 동일하게 설정 하면 그 둘을 서로 매핑 해주었는데,
이클립스와 STS는 자동 매핑 기능을 지원 하지 않는듯 하다.
 
해결 하기 위해서는 아래의 플러그인을 설치해야지 매퍼 인터페이스와 매퍼 xml 파일을 자동 매핑 할 수 있게 된다고 한다.
 

 

 
플러그인을 설치 하려면, 버전 호환성을 체크 해야 했기에, 부담 요소가 있어서,
 
기존에 applicationContext.xml 파일 설정을 아예 다른 방식으로 변경해서 해결 할 수 있었다.
 
설정 과정 및 방법은 이 게시글의 본문 내용과 동일 하기에 생략 하겠다.
 
이 네번째 이슈로 인해 기존 설정과 다른 설정을 하게 되었는데,
개념적으로 차이점이 생겼기에, 아래에 정리하고자 한다.
 
결론부터 말하면,
매퍼 xml 파일이 존재하는 경로를 applicationContext.xml 에서 매퍼 인터페이스가 있었던 패키지를 스캔 하는 
 

<mybatis-spring:scan base-package="com.spring.mapper"/>

 
위의 방식이 아니라, (매퍼 인터페이스와 매퍼 xml 파일을 자동 매퍼 할 수 없으니까)
 

    <!-- SqlSession -->
    <!-- config 설정 파일 경로 설정 -->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="configLocation" value="classpath:mybatis-config.xml" />
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- SqlSessionTemplate -->
    <!-- 생성자 주입 방식 -->
    <bean class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg ref="sqlSession"/>
    </bean>

 
위와 같은 설정을 하여 해결 할 수 있게 되었다.
 
자세한 내용은 마찬가지로 이 게시글의 본문 내용과 동일 하기에 생략 하겠다.

반응형