학습 키워드 : 자바코드와 쿼리의 분리 &쿼리매퍼인 Mybatis
Mybatis를 왜 이용할까?
JdbcTemplate 사용시 query가 자바코드에 하드코딩되는 문제가 있다.
이때 쿼리매퍼인 Mybatis를 사용하면, 자바 코드와 쿼리를 분리해준다.
쿼리수정을 위하여 자바코드를 건드는 짓을 하지 않아도 된다는 것이다!
Springboot에서 Mybatis를 이용하는 두가지 방법
■ Annotation을 이용하는 방법
@Insert, @Update @Select annotation의 파라미터로 query를 넣어준다.
■ Xml을 이용하는 방법
xml파일에 query를 작성해주고, 어떤 Mapper와 매핑되는지 명시해준다.
여기서는 xml파일을 이용하는 방법을 기록한다.
먼저 과정을 요약하자면 이렇다. (전체 글 내용임)
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
■ dependency 설정
■ 설정파일(yml)에 mybatis 설정
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
■ 쿼리를 관리할 파일인 xml 작성
■ ResultSet 결과를 매핑할 자바객체 만들기
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
■ xml에 작성했던 쿼리와 매핑되는 Mapper Interface 작성
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
✨ 그러면 우리가 사용할 쿼리문은 xml파일에서 한번에 관리할 수 있고,
Mapper Interface에서 해당 쿼리문과 매핑되는 메서드를 작성함으로써 쿼리문을 사용할 수 있다.
■ Dependency 설정하기
mybatis를 쓰기 위해 spring-boot-starter 패키지를 이용한다.
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.0</version>
</dependency>
■ mybatis 설정 작성하기
위와 같이 스프링부트가 제공하는 mybatis 의존성을 추가했다면
Springboot Autoconfiguration에 의해서 자동적으로 mybatis 세팅이 가능하기 때문에,
yml에 다음과 같이 mybatis 설정 내용을 명시해준다.
#jdbc driver에 대한 설정
spring:
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:~/test;
username: sa
password:
#mybatis 설정
mybatis:
type-aliases-package: com.example.demo.repository.domain
configuration:
map-underscore-to-camel-case: true
default-fetch-size: 100
default-statement-timeout: 30
mapper-locations: classpath:mapper/*.xml
위 mybatis 설정 설명 (더보기)
mybatis:
type-aliases-package: com.example.demo.repository.domain
: jdbc api 사용시 쿼리 결과인 ResultSet을 사용하는 것은 번거로웠다. (getString해서 필드 가져오는 등)
: 쿼리 결과인 ResultSet을 자바 객체에 매핑해주면 편할 것이다.
: ResultSet 매핑을 원하는 자바객체가 있는 위치를 명시하는 부분이다.
configuration:
map-underscore-to-camel-case: true
: db상에 snake case로 작성한 것을, 자바 객체에 가져올 때 camel case로 매핑시켜 본다.
default-fetch-size: 100
: 예를 들어 select all 해서 한번에 가져올 때, 최대 100개 가져오겠다는 말이다.
default-statement-timeout: 30
: statement 객체가 db와 통신할 때 timeout을 30초로 설정
mapper-locations: classpath:mapper/*.xml
: 쿼리 관리를 하는 위치! 나는 xml로 관리하기로 했다.
위 설정파일에 명시한 것 중 다음 두가지에 집중해보자.
ⓐ mapper-location 설정은, 쿼리를 관리할 xml파일의 위치 설정이다.
ⓑ type-aliases-package 설정은, ResultSet과 매핑할 자바객체 위치 설정이다.
그렇다면 다음에 해야할 것도 다음의 두가지겠다.
ⓐ 사용할 쿼리문들을 작성할 xml 파일 만들기
ⓑ ResultSet과 매핑할 자바객체 만들기
즉 '사용할 쿼리'와 ResultSet을 매핑에 사용할 타입인 '자바 객체'를 만드는 것이다.
당연히 자바 객체는 개인의 상황에 맞게 만들면 된다.
나는 실습에서 Customer 객체를 사용하고 있기 때문에 Customer로 매핑시켜줄 것이다.
그럼 쿼리파일(xml)과, ResultSet 매핑 결과로 사용할 자바객체를 만들어보자.
(이때, 당연히 위 yml파일에 명시한 위치에 만들어야 함!)
■ 쿼리를 관리할 파일(xml)
아래와 같이 구성된다.
쿼리문이 insert문이면 insert 태그, update문이면 update 태그를 사용하고 있다.
그리고 각 쿼리문은 id를 부여한다.
이 쿼리문을 사용하고자 할 때, 각각의 id로 접근하게 된다. 곧 이곳의 쿼리를 불러 사용하기 위한 Mapper interface를 하나 만들 예정인데, 그 Mapper interface의 메서드명을 각 쿼리의 id와 동일하게 지정하면 된다.
<mapper namespace="~~~"> 태그에는, 이 xml파일에 있는 쿼리를 매핑해 사용할 Interface를 지정해준다!
>> 이 Mapper Interface에는 해당 쿼리 id를 메서드명으로 하는 메서드를 둘 것이다.
>> 이 Interface의 메서드를 호출함으로써 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.example.demo.repository.CustomerMapper">
<insert id="save">
INSERT INTO customers (id, first_name, last_name)
VALUES (#{id}, #{firstName}, #{lastName})
</insert>
<update id="update">
UPDATE customers
SET first_name=#{firstName},
last_name=#{lastName}
WHERE id = #{id}
</update>
<select id="findById" resultType="customers">
SELECT *
FROM customers
WHERE id = #{id}
</select>
<select id="findAll" resultType="customers">
SELECT *
FROM customers
</select>
</mapper>
■ ResultSet 매핑 결과로 사용할 자바객체
각자 ResultSet을 어디에 매핑시킬 건지, 필요한대로 만들면 된다.
내 실습에서는 다음 Customer객체에 ResultSet을 매핑시킨다.
// ResultSet의 결과를 이 객체에 매핑할 것이다!
@Alias("customers")
public class Customer {
private long id;
private String firstName;
private String lastName;
// Mybatis는 기본적으로 Getter, Setter로 동작하므로, 넣어주어야 한다!
}
■ Mapper Interface
방금 위에서 쿼리파일(xml)과, 내가 ResultSet을 매핑해서 사용할 자바객체 Customer를 만들었다.
이제 자바 코드에서 쿼리문을 이용하기 위해서 Mapper interface를 만들 것이다. 방금 xml에 작성한 각 쿼리의 id를, Mapper interface의 메서드명으로 두면, 해당 쿼리문을 자바코드로 불러 사용할 수 있다.
쉬운 이해를 위해 도식화해봤다.
그러면 Mapper interface는 다음과 같이 구성하게된다.
// 실제 사용할 쿼리들과 매핑되는 메서드들을 만들어보자.
// xml파일에 id와 함께 명시한 쿼리들은 id명과 일치하는 이름의 메서드에 매핑될 것이다.
@Mapper
public interface CustomerMapper {
void save(Customer customer);
Customer findById(long id);
}
최종
방금 만든 Mapper Interface를 통해서 xml에 명시한 쿼리문 수행해보기!
아래 테스트코드를 통해 간단히 확인했다.
@Slf4j
@SpringBootTest
public class MapperTest {
@Autowired
JdbcTemplate jdbcTemplate;
@Autowired // (xml의 쿼리문과 매핑되는) Mapper Interface를 주입!
CustomerMapper customerMapper;
@Test
void sql_test() {
// 그냥 테이블 드롭하고 생성해주는 부분을 넣어둔 것 (이 부분은 그냥 sql을 직접 작성한 상태)
jdbcTemplate.update("DROP TABLE customers IF EXISTS");
jdbcTemplate.update("CREATE TABLE customers(id SERIAL, first_name VARCHAR(255), last_name VARCHAR(255))");
// Mapper Interface를 통해 xml에 작성한 쿼리문을 사용할 수 있게 된다.
customerMapper.save(new Customer(1L, "YEEUN", "KIM"));
Customer customer = customerMapper.findById(1L);
// 꺼내온 자바 객체 확인해보기
log.info("Name => {} {}", customer.getFirstName(), customer.getLastName());
}
}
log 찍힌 것 확인해보기 :
요약!
✨ 우리가 사용할 쿼리문을 xml파일에 작성해두었다.
✨ 따라서 쿼리문 수정을 위해 자바 코드를 건들 필요가 없어졌다.
✨ Mapper Interface에서 xml의 쿼리문과 매핑되는 메서드를 작성함으로써 쿼리문을 사용할 수 있다.
✨ 쿼리매퍼로는 Mybatis를 사용했고, yml파일에 mybatis 설정 정보를 작성했다. 이때, 쿼리를 관리할 xml 파일의 위치도 명시했다.
✨ 쿼리매퍼를 통해, xml 파일에 명시한 쿼리들을
✨ 쿼리매퍼를 통해, 쿼리 결과를 자바객체로 쉽게 매핑해 가져올 수 있었다.
한계점?
저번 글과 이번 글에서 공부한 JdbcTemplate, QueryMapper는 공통된 한계점이 있다.
관계형 DB와 자바객체가 자유롭게 연결되지 못한다는 것이다.
JdbcTemplate에서도 다소 번거롭게 ResultSet을 다뤄서 자바객체와 연결시켜야 했고,
사실 이번 QueryMapper를 사용하는 것도 수작업을 통해서 자바객체와 연결시켜야 했다.
이를 극복하기 위해 JPA를 사용한다.
관계형 DB와 자바객체의 패러다임을 일치시킬 수 있는 것이다.
'JAVA > Application' 카테고리의 다른 글
[Springboot] thymeleaf-extras-springsecurity5 dependency 못 읽어오는 문제 (0) | 2023.02.03 |
---|---|
[JPA] Entity는 영속성 컨텍스트에 저장/조회한다! (4) | 2023.01.25 |
Spring MVC | Controller는 어떻게 요청을 처리하는 걸까? | Controller와 Servlet (0) | 2023.01.19 |
Spring AOP 쉽게 이해하기 | 그리고 간단한 예제코드 (0) | 2023.01.16 |
post 요청 후 406 Not Acceptable 해결 - Dto에 Getter 붙이기 (0) | 2023.01.16 |