개발자들은 데이터 베이스에 데이터를 유지하기 위해 JDBC를 이용하여 데이터베이스에 접근하였다. JDBC의 복잡함을 단순하게 하게 위해 Persistence FrameWork를 사용했는데 ORM 프레임워크와 SQL 매퍼이다.
ORM 프레임워크는 SQL 코드를 직접 작성하지 않고 자바에서 객체지향적인 방식으로 데이터를 객체로 다루면 이를 관계형 데이터베이스에서 작업할 때 SQL로 변환하여 연동을 할 수 있도록 하는 것이다. 즉 SQL을 직접 작성할 필요가 없는 것이다. 데이터베이스에 종속성이 약해서 바뀌더라도 바로바로 적용 가능하다. 대표적으로는 Hibernate가 있고 이에 대한 표준 기술 명세가 JPA이다.
MyBatis는 ORM 프레임워크라고 말하지는 않고 SQL 매퍼라고 한다. 객체와 관계형 데이터베이스의 데이터를 개발자가 작성한 SQL로 매핑시켜주는 프레임워크이다. 개발자가 SQL을 직접 작성해야 하며 SQL문을 실행하고 얻은 데이터를 객체로 매핑시켜준다.
그럼 이제 MyBatis를 스프링 부트 프로젝트에 적용시켜 보자.
Gradle에선 아래와 같이 설정한다.
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter'
Maven에선 아래와 같이 설정한다.
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
매번 DB Connection 을 하는 것은 좋지 않기 때문에 커넥션 풀을 설정해서 미리 연결을 통해 맺은 객체들을 풀에 저장해 놓았다가 요청이 오면 connection을 해주고, 처리가 다 끝나면 다시 반납해줘서 pool에 저장하는 방식이다. 설정하는 방식은 DataSource를 설정해야 하지만 스프링 부트에서는 aaplication.properties 혹은 .yml에서 설정이 가능하다.
아래와 같이 application.yml 에 기본적인 mysql 설정을 해준다.
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/calendar?serverTimezone=Asia/Seoul
username: calendar
password: calendar
mysql 설정은 https://khdscor.tistory.com/35?category=977203 를 참고하길 바란다.
1. @MapperScan
MyBatis의 가장 핵심적인 객체는 SQLSession와 SQLSessionFactory 객체다. SQLSessionFactory는 내부적으로 SQLSession을 생성해내는데 이 SQLSession을 통해서 Connection을 생성하거나 원하는 SQL을 전달하고, 결과를 리턴 받는 구조로 작성하게 된다. 스프링은 SQLSession와 SQLSessionFactory 객체를 생성하여 빈으로 등록해야 하지만 스프링 부트에서는 메인 애플리케이션에 @MapperScan을 사용하면 스프링 부트가 @Mapper가 붙은 MyBatis 매퍼를 스캔하여 빈으로 등록할 수 있도록 한다.
아래와 같이 스프링 부트 메인 애플리케이션 클래스 @MapperScan을 붙이고 mapper 패키지 경로를 입력한다.
@MapperScan
@SpringBootApplication
public class CalenderApplication {
public static void main(String[] args) {
SpringApplication.run(CalenderApplication.class, args);
}
}
2. Mapper 생성
Mapper는 어노테이션을 이용해서 자바 파일에 구현하는 방법과 xml파일에 구현하는 방법이 있다.
먼저 자바파일에 구현하는 것은 아래와 같다.
화면과 같이 mapper 패키지를 만들고 ArticleMapper 인터페이스를 만들었다. 여기서 중요한 점이 인터페이스가 Mapper라는 것을 인식할 수 있게 @Mapper을 꼭 붙여주어야 한다. 그래야 @MapperScan에서 찾을 수 있다. 만약 스프링 부트가 아닌 스프링을 사용한다면 root-context.xml 파일에 설정을 해주어야 한다.
@Mapper
public interface ArticleMapper {
@Select("SELECT * FROM article WHERE id = #{id}")
Article findById(Long id);
}
위와 같이 findById를 만들었고 이를 사용하는 코드는 아래와 같다.
@Transactional
public void write(Long articleId, Long userId, String content) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new NotExistsUserException("해당되는 유저가 존재하지 않습니다."));
/*Article article = articleRepository.findById(articleId)
.orElseThrow(() -> new NotExistsArticleException("해당되는 게시글이 존재하지 않습니다."));*/
Article article = articleMapper.findById(articleId);
if(article == null)
throw new NotExistsArticleException("해당되는 게시글이 존재하지 않습니다.");
commentRepository.save(Comment.builder()
.content(content)
.user(user)
.article(article)
.build());
}
}
위 wirte는 user와 article를 가지고 comment를 만들어서 저장하는 코드이다. 본래는 JPA를 사용하였지만 Article를 가져오는 과정은 MyBatis를 사용한 것을 알 수 있다. 만약 articleId에 해당되는 article 이 없으면 null을 리턴한다.
그다음 xml을 이용하는 방법을 살펴보겠다. 아래와 같이 resource 안에 패키지와 xml 파일을 만든다.
그리고 yml파일에 아래와 같이 xml 파일의 경로를 설정한다.
mybatis:
mapper-locations: mapper/**/*.xml
그리고 만들었던 ArticleMapper에 메서드를 추가한다.
이제 xml 파일에 findById2에 대한 내용을 작성하면 된다. findById와 똑같은 기능이며 내용은 아래와 같다.
<?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="calender.calender.mapper.ArticleMapper">
<select id="findById2" resultType="calender.calender.domain.Article">
SELECT * FROM article WHERE id = #{id}
</select>
</mapper>
위와 같이 작성하면 findById2를 호출하면 findById와 똑같이 작동할 것이다.
이렇게 두 가지 방식이 있는데 각각 장단점을 가지고 있다. interface파일에 어노테이션을 통해 구현하는 방식은 코드가 매우 간결하지만 복잡한 쿼리문을 짜야할 때에는 이 방식이 좋지는 않다. xml파일에 구현하는 방식은 코드가 길긴 하지만 복잡한 쿼리문을 짤 때 더 유용하다. 그렇기에 상황에 맞게 작성하면 될 것이다.
위에 예시에서는 간단하게 select만을 확인하였고 그 밖에 CRUD 기능도 비슷하게 구현해주면 된다. insert는 void 타입으로, delete는 int 타입인데 삭제하기 위한 데이터가 존재하면 1을, 존재하지 않으면 0을 출력한다. update도 int 타입으로 몇 개의 데이터가 수정됐는지를 확인할 수 있다.
아래는 insert를 사용했을 때의 예시이다.
@Insert("INSERT INTO comment(content, created_date, article_id, user_id) VALUES (#{content}, #{created_date}, #{article_id}, #{user_id})")
void createComment(String content, Long article_id, Long user_id, Date created_date);
참고
https://azurealstn.tistory.com/83
https://dongjuppp.tistory.com/78
https://firework-ham.tistory.com/110
https://devlog-wjdrbs96.tistory.com/200
https://mybatis.org/mybatis-3/ko/dynamic-sql.html
코드로 배우는 스프링 웹 프로젝트 - 구멍가게 코딩단
'mybatis' 카테고리의 다른 글
mybatis - ResultMap Collection을 통해 데이터 한번에 가져오기 (0) | 2023.06.18 |
---|---|
mybatis - insert 후 해당 객체의 바로 PrimaryKey 얻기 (0) | 2023.04.16 |
springboot with mybatis - foreign key로 연결된 행 삭제 (0) | 2023.04.09 |
springboot with mybatis - test 설정(인메모리 db: h2) (0) | 2023.04.05 |
sprintboot with mybatis - 캐시 및 readonly 적용 (0) | 2023.03.05 |