일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- 변경 감지
- K8s
- OneToMany
- chroot exit code
- exit code
- mybatis
- 다중 데이타소스
- 활성프로브
- 종료코드
- Design Pattern
- docker
- 트랜잭션 쓰기 지연
- OneToOne
- JPA
- 디자인 패턴
- Java
- ManyToMany
- ManyToOne
- dirty check
- Multi Transaction
- Multi Datasource
- JDK
- 영속화
- MaxRAMPercentage
- OracleJDK
- openjdk
- SpringBoot 2.0
- SpringBoot
- Entity
- 다중 트랜잭션
- Today
- Total
조금 평범한 개발 이야기
JPA 환경에서 MyBatis 를 사용하여 데이타를 가지고 오기 본문
앞서 JPA 관련된 이야기를 할때 JPA 와 Mybatis 를 비교하면서 서로의 장점과 단점에 대한 이야기를 나눈 적이 있습니다. 그때 JPA 를 사용함으로써 얻는 가장 큰 이점은 드러나지 않아 파악하기 어려운 Query 구문이 로직상으로 도출이 됨으로 인해 향후 유지보수가 용이해지며 테스트를 쉽게 할 수 있는 유연한 구조를 얻을 수 있다는 점 이였지만 복잡한 데이타를 집계 한다거나 여러 Entity 간의 관계를 통한 결과를 도출하려고 하는 용도로는 적합하지 않으며 결국은 Query 를 사용해 데이타를 가지고 올 수 밖에는 없는데 이를 위해 Mybatis 와 JPA 가 같이 사용해야 된다는 점을 이야기 드렸습니다.
이번 글에는 이전 JPA 가 설정된 동일한 소스 코드에 어떻게 MyBatis 를 사용 하는지에 대한 이야기를 해보려 합니다.
먼저 MyBatis 는 Framework 이 아니라 SQL Query Template Library 라는 것을 기억해야 합니다. 개발자가 동적 Query 를 쉽게 작성할 수 있게 관리를 도와주는 Library 이며 MyBatis 자체로는 Query 작성에 아무런 도움을 주진 않습니다. 결국 개발자가 데이타베이스의 특징에 맞는 SQL Query 를 정확하게 이해하고 있어야만 Query 를 작성할 수 있는데 이 때문에 역설적으로 데이타베이스 특성에 맞춘 속도 최적화 같은 작업을 할 수 있게 되는 것 입니다. 물론 그렇기 때문에 발생되는 코드의 비효율은 감안해야 합니다.
물론 JPA 로 동적 쿼리를 작성할 수 없는 것은 아닙니다. JPA 표준으로는 Creteria 라는 문법이 존재하며 보다 명확한 문법을 제시하는 QueryDSL 같은 Library 도 존재합니다. 하지만 복잡한 데이타를 가지고 올때 어디까지 JPA 로 개발 해야 하며 어디부터 MyBatis 로 개발 해야 되는지에 대한 기준은 매우 모호하며 이것은 개발자의 경험에 의존 할 수 밖에는 없다고 봅니다. 개인적인 기준을 이야기 드리자면 리팩토링 법칙 중 2번 이상 반복이 된다면 공통 기능으로 도출해야 된다는 법칙에 의해 2개 이상의 테이블과 조인이 되어야 한다면 MyBatis 로 Query 를 작성해라 고 이야기 드릴 것 같습니다.
PostViewMapper.xml
먼저 MySQL 데이타베이스의 특성에 맞는 SQL Query 구문을 이용해 Post Entity 의 조회 Mapper xml 를 작성해 보겠습니다. 원래 명칭은 PostMapper 로 지정하는것이 맞겠지만 JPA 와 구분을 위해 PostViewMapper 로 작성 하였습니다. 구문은 단순 조회용 Query 이며 post 와 연관이 있는 category, post_detail, post_tag 테이블과 함께 JOIN 으로 데이타를 질의해 데이타를 가지고 오는 속도를 향상 시켰습니다.
<?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="net.jogeum.hellojpa.repository.PostViewMapper">
<sql id="select">
select
a.id,
b.name as category_name,
a.title,
a.content,
c.description,
d.tag,
a.created_by,
a.created_date
</sql>
<sql id="from">
from
post a
inner join
category b
on a.category_id = b.id
left join
post_detail c
on a.id = c.post_id
left join
(
select
e.post_id,
group_concat(e.tag_name) as tag
from
post_tag e
group by
e.post_id
) d
on a.id = d.post_id
</sql>
<sql id="where">
<where>
<if test="type != null">
<if test="type.name() == 'title'">
and a.title like concat('%', #{value}, '%')
</if>
<if test="type.name() == 'category'">
and b.name = #{value}
</if>
<if test="type.name() == 'date'">
and to_char(a.created_date, 'yyyymmdd') >= #{value}
</if>
<if test="type.name() == 'tag'">
and d.tag like concat('%', #{value}, '%')
</if>
</if>
</where>
</sql>
<sql id="order">
order by
a.created_date DESC
</sql>
<select id="getList" parameterType="map" resultType="net.jogeum.hellojpa.domain.PostView">
<include refid="select"/>
<include refid="from"/>
<include refid="where"/>
<include refid="order"/>
</select>
<select id="getCount" parameterType="map" resultType="int">
select count(*)
<include refid="from"/>
<include refid="where"/>
</select>
</mapper>
PostViewMapper
JPA 의 Repository interface 에 대응되는 Mapper interface 를 정의합니다. 이때 interface 상단에 @Mapper 어노테이션을 정의하면 Mybatis 의 SqlSessionFactory 가 해당 어노테이션을 찾아와 Mapper 의 구현체를 정의하고 관리해 줍니다.
@Mapper
@Repository
public interface PostViewMapper {
List<PostView> getList(Map<String, ?> params);
Integer getCount(Map<String, ?> params);
}
PostViewService
그런 다음 마지막으로 Mapper 를 사용하는 Service 를 작성합니다. Mapper 의 사용은 별다른 구현 없이 Mybatis 의 SqlSessionFactory 가 @Mapper 가 정의되어 있는 객체를 찾아와 정의한 구현체를 그대로 사용하면 됩니다.
@Slf4j
@Service
public class PostViewService {
@Autowired
PostViewMapper postViewMapper;
@Transactional(rollbackFor = Exception.class)
public List<PostViewDTO> getList(PostSearchType type, String value) {
Map<String, Object> params = new HashMap<>();
if (!StringUtils.isEmpty(type) && !StringUtils.isEmpty(value)) {
params.put("type", type);
params.put("value", value);
}
return postViewMapper
.getList(params).stream()
.map(this::dto)
.collect(Collectors.toList());
}
private PostViewDTO dto(PostView post) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
return new PostViewDTO(
post.getId(),
post.getCategoryName(),
post.getTitle(),
post.getContent(),
post.getDescription(),
post.getTag(),
post.getCreatedBy(),
post.getCreatedDate().format(formatter)
);
}
}
소스 상에 데이타를 가지고 오는 방법 중 JPA 로 구현된 PostController 와 MyBatis 로 구현된 PostViewController 를 서로 비교해 보면서 확인 하시는 것도 좋을 것 같습니다.
소스코드
앞서 설명한 JPA 환경 구성과 MyBatis 의 사용 예제에 대한 소스 코드는 아래 주소에서 확인 하실 수 있습니다.
'개발 > 쉽게 이해하고 사용하는 JPA' 카테고리의 다른 글
JPA 저장과 영속화 과정에 대해서 (0) | 2021.07.28 |
---|---|
JPA Repository 를 이용한 데이타 사용 (1) | 2018.10.17 |
JPA 관계와 그 사용법에 대해 (양방향) (0) | 2018.10.10 |
JPA 관계와 그 사용법에 대해 (단방향) (4) | 2018.10.09 |
JPA 기본 사용 문법 파악하기 (0) | 2018.10.01 |