
MyBatis 프레임워크란 ?
데이터베이스와 상호작용하며, 쿼리문을 쉽게 작성하고 실행할 수 있도록 도와주는 프레임워크 이다.
MyBatis는 데이터베이스와 자바 객체 간의 매핑을 간단하게 처리 할 수 있도록 도와 준다.
MyBatis 프레임워크의 장점
.xml 파일로 쿼리문 관련 설정 및 작성을 하기 때문에,
결합도가 낮아지고,
응집도가 높아지며,
유지 보수가 용이한 프로그래밍 개발을 지향 하게 된다.
왜 ?
.xml 파일을 사용 한다는 의미는 JAVA 언어와의 분리를 의미 한다.
따라서,
JAVA와 관련성이 없어짐에 따라, 쿼리문을 수정하더라도 컴파일을 다시 할 필요가 없다.
MyBatis 설정 하기
1.

Spring Starter Project 클릭.
2.

설정 하고, Next 클릭.
3.

MyBatis Framework 를 같이 체크 하고 Finish 클릭.
4.

application.properties 에 MyBatis 관련 설정 하기.

위의 코드를 추가해준다.
mybatis 폴더 (패키지) 내부에 있는 mapper 폴더 (패키지) 내부를 의미 하는 경로 설정 이고,
**은 파일 이름은 모르지만 모든 파일을 가리킨다는 의미 이며,
**.xml 은 모든 파일의 확장자는 .xml 파일이라는 것을 의미하는 설정 이다.
5.

implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.2'
build.gradle 내부에 dependencies 에 위의 코드를 추가 한다.
추가 하고, gradle 설정 파일을 새로고침 해준다.

6.
MyBatis .xml 설정 파일은 아래와 같이 설정 한다.

내부에 작성하는 Xxx.xml 파일의 이름은 자유지만, 일반적으로 DAO 클래스의 이름과 동일하게 작성 하기도 한다.

MyBatis 파일 작성 하기
1.
.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">
// 예시
<mapper namespace="com.hwan.app.model.InterfaceBoardDAO">
<insert id="insert"> .. </insert>
<select id="selectAll"> .. </select>
<select id="selectOne"> .. </select>
<update id="update"> .. </update>
<delete id="delete"> .. </delete>
</mapper>
스키마 :
.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">
루트 엘리먼트 :
== 요소 == 태그
<mapper namespace=""> .. </mapper>
namespace 라는 속성이 있다.
어떤 클래스 (인터페이스) 를 구현 하고 있는지에 대한 속성을 의미 한다.
이때, 이 어떤 클래스 (인터페이스) 에 @Mapper 어노테이션을 명시 해서 사용 한다.
@Mapper 어노테이션 이란 ?
MyBatis와 관련된 인터페이스를 스프링 프레임워크에서 자동으로 스캔하고,
MyBatis 의 매퍼로 등록하는데 사용 한다.
이 어노테이션은 스프링의 컴포넌트 스캔 및 자동 빈 등록 기능을 활용하여,
MyBatis 매퍼를 설정 파일에 등록 하지 않아도 사용 할수 있도록 도와준다.
결론적으로,
@Mapper 어노테이션을 사용하면,
MyBatis 에서 매퍼 인터페이스를 스프링 빈으로 등록 하고,
MyBatis 에서 사용 할 수 있도록 하는 프로세스를 단순화 하고 자동화 한다.
예시 01)
@Mapper
public interface InterfaceMemberDAO {
boolean insert(Map<String,String> map);
List<MemberDTO> selectAll(MemberDTO memberDTO);
MemberDTO selectOne(Map<String,String> map);
boolean update(Map<String,String> map);
boolean delete(String mid);
} // InterfaceMemberDAO
예시 02)
@Mapper
public interface InterfaceBoardDAO {
boolean insert(Map<String,String> map);
List<BoardDTO> selectAll(@Param("MID") String mid);
BoardDTO selectOne(@Param("BID") int bid);
boolean update(Map<String,String> map);
boolean delete(@Param("BID") int bid);
} // InterfaceBoardDAO
내부 엘리먼트 :
== 요소 == 태그
<insert id="insert"> .. </insert>
<select id="selectAll"> .. </select>
<select id="selectOne"> .. </select>
<update id="update"> .. </update>
<delete id="delete"> .. </delete>
일반적으로 5개의 요소 == 태그 == 엘리먼트 를 가진다.
C / R / R / U / D
insert / selectAll / selectOne / update / delete
내부 엘리먼트의 속성인 id 속성으로는
@Mapper 어노테이션이 존재하는 클래스 (인터페이스) 내부의 메서드명으로 작성 한다.
>> 메서드 구분을 위해서 이렇게 작성하도록 한다.
내부 엘리먼트 안에 작성할 수 있는 속성은 다양 하게 존재 한다.

이중에서 resultType 속성은 다음과 같은 기능을 수행 한다.
MyBatis에서 SQL 쿼리문의 실행 결과를 매핑할 Java 객체의 타입을 지정 한다.
이 속성을 사용하여 SQL 쿼리문의 결과를 어떤 형태로 매핑할 것인지를 명시 할 수 있다.
즉,
매핑할 Java 클래스 경로를 지정하면, 지정된 클래스는 SQL 쿼리의 결과와 매핑된다.
따라서,
테이블의 칼럼을 받을 Java의 프로퍼티 클래스 == VO == DTO 클래스 에 지정 하면 되며,
쿼리문의 결과를 매핑 하는 기능이기 때문에,
selectAll 또는 selectOne 과 함께 사용 된다.
서비스 레이어 내부 코드 작성 방법
MyBatis (기존 DAO 클래스) 를 사용 하는,
@Service 어노테이션이 명시 되어 있는 서비스 레이어 내부에
코드를 작성 하는 방법으로 여러가지가 있다.
예시를 보며 확인 해보자.
예시 01)
@Service
public class BoardService implements InterfaceBoardService {
@Autowired
private InterfaceBoardDAO interfaceBoardDAO;
@Override
public boolean insert(BoardDTO boardDTO) {
Map<String,String> map = new HashMap<String,String>();
map.put("data1", boardDTO.getMid());
map.put("data2", boardDTO.getContent());
return interfaceBoardDAO.insert(map);
}
@Override
public List<BoardDTO> selectAll(BoardDTO boardDTO) {
return interfaceBoardDAO.selectAll(boardDTO.getMid());
}
@Override
public BoardDTO selectOne(BoardDTO boardDTO) {
return interfaceBoardDAO.selectOne(boardDTO.getBid());
}
@Override
public boolean update(BoardDTO boardDTO) {
Map<String,String> map = new HashMap<String,String>();
map.put("data1", boardDTO.getContent());
map.put("data2", String.valueOf(boardDTO.getBid()));
return interfaceBoardDAO.update(map);
}
@Override
public boolean delete(BoardDTO boardDTO) {
return interfaceBoardDAO.delete(boardDTO.getBid());
}
} // BoardService
insert() 메서드와 update() 메서드를 보면,
반환값으로 사용되는 각각의 CRUD 메서드의 인자로 map 컬렉션 프레임워크를 사용한것을 볼 수 있다.
이것은 쿼리문에 동적으로 필요한 기존의 ? (물음표) 값이 다수 일 때 사용되는 방식으로,
필요한 값이 많을 경우 당연히 인자가 길어져서 가독성이 떨어지므로,
map 컬렉션에 필요한 데이터를 put() 해서 사용하는 방식으로 생각 하면 된다.
map 컬렉션의 제네릭은 일반적으로 String 타입으로 사용 되며,
웹 개발 에서는 특히 더더욱 주고받는 데이터 타입을 String 타입으로 많이 사용 한다.
뿐만아니라, 최근에는 위와 같이 map 컬렉션을 활용한 방식으로 많이 사용 되고 있다.
boardDTO.getBid() 는 int 타입이므로, String.valueOf() 메서드를 사용해서,
int 타입의 값을 String 타입으로 변환 하는 모습을 확인 할 수 있다.
이때,
MyBatis는 map 컬렉션에 존재하는 데이터를 자동으로 매핑하는 작업을 수행 하는데,
DB의 칼럼의 타입과 일치하는 프로퍼티의 타입이 일치 하지 않더라도,
MyBatis는 데이터베이스에 적합한 형식으로 자동 형변환을 수행 한다.
해당하는 .xml 내부 파일에는 아래와 같이 코드를 작성 한다.


그리고,
각각의 CRUD 메서드들은 InterfaceBoardDAO 라는 인터페이스를 사용 하기 때문에,
InterfaceBoardDAO 인터페이스 내부에 작성되어 있는 CRUD 메서드의 인자를 수정해 줘야 한다.
사실, 순서상으로는 이곳을 먼저 수정 하고, 서비스 레이어의 CRUD 메서드를 작성하는게 맞을듯 하다.
@Mapper
public interface InterfaceBoardDAO {
boolean insert(Map<String,String> map);
List<BoardDTO> selectAll(@Param("MID") String mid);
BoardDTO selectOne(@Param("BID") int bid);
boolean update(Map<String,String> map);
boolean delete(@Param("BID") int bid);
} // InterfaceBoardDAO
위의 코드를 보면, insert() 와 update() 메서드의 인자로 map 컬렉션이 사용된것을 볼 수 있다.
다음으로,
selectAll(), selectOne(), delete() 메서드를 보면,
쿼리문에 동적으로 필요한 값만 인자로 작성된 것을 확인 할 수 있다.
이때,
InterfaceBoardDAO 내부에서 @Param 어노테이션을 작성한것을 확인 할 수 있다.
이는,
@Param 어노테이션을 사용함으로써, MyBatis에서 매개 변수를 인식 할 수 있게 하고,
쿼리문에 사용하기 위한 값의 이름을 명시적으로 작성하는 방식이라고 생각 하면 된다.
따라서, @Param 어노테이션을 작성한 방식일 때, 해당하는 .xml 내부 파일에는 아래와 같이 코드를 작성 한다.


반면에,
그냥 파라미터로 변수를 넘기는 방법이 존재 한다. 아래를 확인 하자.
package com.hwan.app.service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.hwan.app.model.InterfaceMemberDAO;
import com.hwan.app.model.MemberDTO;
@Service
public class MemberService implements InterfaceMemberService {
@Autowired
private InterfaceMemberDAO interfaceMemberDAO;
@Override
public boolean insert(MemberDTO memberDTO) {
Map<String,String> map = new HashMap<String,String>();
map.put("data1", memberDTO.getMid());
map.put("data2", memberDTO.getMpw());
return interfaceMemberDAO.insert(map);
}
@Override
public List<MemberDTO> selectAll(MemberDTO memberDTO) {
return interfaceMemberDAO.selectAll(memberDTO);
}
@Override
public MemberDTO selectOne(MemberDTO memberDTO) {
Map<String,String> map = new HashMap<String,String>();
map.put("sk", memberDTO.getSk());
map.put("data1", memberDTO.getMid());
map.put("data2", memberDTO.getMpw());
return interfaceMemberDAO.selectOne(map);
}
@Override
public boolean update(MemberDTO memberDTO) {
Map<String,String> map = new HashMap<String,String>();
map.put("data1", memberDTO.getMpw());
map.put("data2", memberDTO.getMid());
return interfaceMemberDAO.update(map);
}
@Override
public boolean delete(MemberDTO memberDTO) {
return interfaceMemberDAO.delete(memberDTO.getMid());
}
} // MemberService
쿼리문에 필요한 동적인 값이 단일 값인 delete() 메서드를 보면,
@Mapper
public interface InterfaceMemberDAO {
boolean insert(Map<String,String> map);
List<MemberDTO> selectAll(MemberDTO memberDTO);
MemberDTO selectOne(Map<String,String> map);
boolean update(Map<String,String> map);
boolean delete(String mid);
} // InterfaceMemberDAO
그냥 파라미터로 변수를 넘기는 것을 볼 수 있다.
이때는,

이렇게 작성 할 수 있다.
@Param 어노테이션을 사용한 경우 VS @Param 어노테이션을 사용하지 않은 경우
@Param 어노테이션을 사용하면,
변수명과 쿼리문에서 사용할 변수명을 분리할 수 있다.
그래서 변수명을 변경하더라도 쿼리문을 변경 하지 않아도 된다.
이게 무슨말이냐면 예시를 통해 확인해보자.
BoardDTO selectOne(@Param("BID") int bid);
@Param("BID") 를 작성함으로써, int bid의 매개변수명을 BID로 지정 한다.
그러면, SQL 쿼리문에서 BID를 사용하여 매개변수와 상호작용 하게 된다.
만약에 이렇게 지정 하지 않을 경우,
int bid의 변수 이름이 변경 되면,
.xml 파일 내부에서도 변수명을 변경해줘야하는 번거로움이 생기게 된다.
하지만,
이름을 지정하면,
int bid의 변수 이름이 변경 되더라도,
무조건 그 변수의 매개변수명은 BID 로 고정이기 때문에,
.xml 파일 내부에서 변수명을 변경해줄 필요가 없게 된다.
즉,
코드의 가독성이 올라가고, 코드의 유지보수성이 향상 된다.
반면에,
@Param 어노테이션을 사용하지 않으면,
변수의 순서와 쿼리문의 변수 순서가 정확하게 일치 해야 한다.
그래서 변수를 추가하거나 삭제 할 때 주의 해야 한다.
그래도 비교적 간단하게 작성 할 수 있다는 이점이 있긴 하다.
[ 참고 ] 기존 코드에서 달라진 점 ?
1.
MyBatis 프레임워크를 DAO 클래스 대신에 사용함으로써,
자바 내부에 생성했던 DAO 클래스가 필요 없게 됬다.
따라서, SpringBoot가 루트컨테이너로 서비스레이어 류를 메모리에 올릴 때,
DAO 클래스는 메모리에 올릴 필요가 없게 된다.

기존에는 위와 같이, @Repository 어노테이션을 사용 해서 메모리에 올려줬지만,

MyBatis 프레임워크를 사용하면서, @Repository 어노테이션을 제거 하게 됬다.
2.
MyBatis 를 사용하는 방식 중,
map 컬렉션을 이용 하는 방법과,
@Param 어노테이션을 사용 하는 방법과,
그냥 단순 파라미터명을 작성 하는 방법이 있었다.
따라서, 더이상 메서드 시그니쳐가 고정되지 않기 때문에, 인터페이스를 구현 하지 않게 됬다.

InterfaceMemberDAO 인터페이스를 구현한 MemberDAO 클래스 이였지만,

인터페이스를 더이상 구현 하지 않는다.
3.
MyBatis 를 사용하는 방식 중,
map 컬렉션을 이용 하는 방법과,
@Param 어노테이션을 사용 하는 방법과,
그냥 단순 파라미터명을 작성 하는 방법이 있었다.
그래서 아래와 같이 인터페이스 내부 추상메서드의 인자를 입맛대로 작성 할 수 있다.

기존의 메서드 시그니쳐가 고정된 모습에서,

MyBatis 프레임워크를 사용함으로써, 메서드 시그니쳐가 자유로워진 것을 볼 수 있다.
[ 참고 ] ibatis 란?

ibatis는 MyBatis 를 의미 한다.
Oracle이 ibatis를 인수 하면서, 이름이 MyBatis로 변경 됬다.
그래서 예전에는 MyBatis를 ibatis 라고 불렀다.
'SpringBoot > 이론' 카테고리의 다른 글
[ SpringBoot ] MyBatis 동적 쿼리 (0) | 2023.09.25 |
---|---|
[ Spring Boot ] 서비스 레이어와 인터페이스의 장점 (0) | 2023.09.21 |
[ Spring Boot ] JDBC 연결 (0) | 2023.09.19 |
[ Spring Boot ] 데이터 유효성 검사 (1) | 2023.09.15 |
[ Spring Boot ] C - V 간의 다양한 방법으로 데이터 주고 받기 (0) | 2023.09.14 |