JPA 와 Mybatis 같이 사용하기
안녕하세요.
JPA 는 ORM 으로 단순 CRUD 처리에 대해 편리하게 해주는데요.
실무에서 정산 쿼리나, 복잡한 여러 조인이 필요한 쿼리들이 있을 때에는, JPA 보단 mybatis를 혼용해서 쓴다고 합니다.
그래서 지금 JPA 와 MyBatis 를 혼용하여 쓰고, 한 트랜잭션 안에 잘 묶여있는지 확인 테스트 까지 해보겠습니다.
데이터베이스는 h2 database 를 사용하겠습니다. (경량용 디비로 테스트 하기에 적합함.)
먼저 Springboot 프로젝트를 만들어 준 후,
의존성 주입을 해줍니다.
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.2'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
의존성 정보는 이렇게 됩니다.
- spring data jpa
- mybatis
- h2
이제 application.yml 에 jpa 및 h2 database를 사용하기 위한 기본 설정을 해보겠습니다.
spring:
h2:
console:
enabled: true
path: /mix
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:tcp://localhost/~/mix
username: sa
password:
jpa:
hibernate:
ddl-auto: update
properties:
hibernate:
show_sql: true
format_sql: true
이렇게 하면 h2 연동 및 jpa 를 사용하겠다는 설정이 완료되었습니다.
h2 연동 되어 jpa를 사용하겠다는 설정이 되었는지 확인이 필요하다고요? 확인해봅시다.
MemberRepository.java
JPA 사용하기 위해 레파지토리 세팅 합니다.
public interface MemberRepository extends JpaRepository<Member,Long> {
}
Member.java 엔티티를 만들어줍시다.
@Entity
@Getter
public class Member {
@Id@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String nickName;
MemberController.java 테스트 한번 해봅시다.
@RestController
@RequiredArgsConstructor
@Slf4j
public class MemberController {
private final MemberRepository memberRepository;
//jpa - insert
@PostMapping("/test")
public void addMemberJPA(){
Member member = new Member();
member.addMember("김영재","닉네임");
memberRepository.save(member);
}
addMember 메소드가 필요하니, Member에 아래 코드를 추가해줍시다.
@Entity
@Getter
public class Member {
@Id@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String nickName;
public void addMember(String name, String nickName) {
this.name = name;
this.nickName = nickName;
}
콘솔에 보면
hibernate가 쿼리를 만들어 줬네요.
jpa가 정상적으로 연동되어 h2에 담겼다는 걸 알수 있습니다.
이제 jpa 연동을 확인 하였으니, mybatis를 연동 해 봅시다.
보통 jpa는
controller -> service -> repository
mybatis는
controller -> service -> mapper -> xml
순서로 이루어져있습니다.
먼저 mybatis 설정을 위해 config를 작성해 줍시다.
MybatisConfig.java (sql session 에 대한 관련 기능을 대신해주는 sqlSessionFactory Bean을 생성합니다.)
@Configuration
@MapperScan(basePackages={"com.example.mybatismixjpa.mapper"}, sqlSessionFactoryRef="sqlSessionFactory",sqlSessionTemplateRef = "sqlSessionTemplate")
public class MybatisConfig {
@Bean(name="sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactoryBean.setTypeAliasesPackage("com.example.mybatismixjpa");
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:/mybatis/*.xml"));
return sqlSessionFactoryBean.getObject();
}
}
ln 2: basePackages에 Mapper 인터페이스 패키지 위치를 작성합니다.
ln 6: 기존에 이용하고 있던 dataSource를 주입받습니다. 현재 주입하는 dataSource는 application.yml 프로퍼티에 설정한 DB 정보를 통해 자동 생성된 dataSource입니다.
ln 9: setTypeAliasesPackage 설정을 통해 엔티티의 패키지 이름을 생략할 수 있도록 합니다.
ln 10: resources 아래의 mapper 폴더 내의 *-mapper.xml 파일들을 SQL Map XML파일로 설정합니다.
기본 mybatis 설정이 끝입니다.
이제 다시
MemberController.java 로 가서 mybatis 연동을 해줍시다.
private final MemberMapper mapper;
@GetMapping("/test2")
public List<Member> selectMemberMybatis(){
List<Member> results = mapper.findById();
log.info("results : {}" , results);
return results;
}
MemberMapper.java
@Mapper
public interface MemberMapper {
List<Member> findById();
}
이제 중요한 XML를 작성할 차례입니다. (실제 쿼리를 작성하는 부분입니다.)
경로는
resources -> mybatis -> MemberMapper.xml 입니다.
MemberMapper.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.mybatismixjpa.mapper.MemberMapper">
<select id="findById" resultType="Member">
SELECT id,
name,
nick_name as nickName
from member;
</select>
결과를 보면
mybatis로 정상적으로 조회가 되는 것을 확인 할 수 있습니다.
이렇게 jpa 와 mybatis를 혼용해서 사용해 보았습니다.
보통 단순한 쿼리는 jpa 를 사용하며, 조금 더 복잡해지면 querydsl 를 사용하여 해결을 할 수 있지만,
엄청 복잡한 쿼리(정산 쿼리)나 그런 쿼리들은 mybatis가 효율적이라고 하네요.
//jpa - insert
@PostMapping("/test")
public void addMemberJPA(){
Member member = new Member();
member.addMember("김영재","닉네임");
memberRepository.save(member);
}
//jpa - select
@GetMapping("/test")
public List<Member> selectMemberJPA(){
return memberRepository.findAll();
}
//jpa - update
@PutMapping("/test")
@Transactional
public void updateMemberJPA(){
MemberUpdateDto memberUpdateDto = new MemberUpdateDto(3L,"수정자김영재");
Member member = memberRepository.findById(memberUpdateDto.getId()).get();
member.updateMember(memberUpdateDto);
}
//jpa - delete
@DeleteMapping("/test")
public void deleteMemberJPA(){
MemberUpdateDto memberUpdateDto = new MemberUpdateDto(3L,"수정자김영재");
Member member = memberRepository.findById(memberUpdateDto.getId()).get();
memberRepository.delete(member);
}
//mybatis - select
@GetMapping("/test2")
public List<Member> selectMemberMybatis(){
List<Member> results = mapper.findById();
log.info("results : {}" , results);
return results;
}
//mybatis - insert
@PostMapping("/test2")
public void addMemberMybatis(){
Member member = new Member();
member.addMember("김영재-마이바티스","닉네임");
mapper.addMember(member);
}
//mybatis - update
@PutMapping("/test2")
public void updateMemberMybatis(){
MemberUpdateDto memberUpdateDto = new MemberUpdateDto(3L,"수정자김영재");
mapper.updateMember(memberUpdateDto);
}
//mybatis - delete
@DeleteMapping("/test2")
public void deleteMemberMybatis(){
mapper.deleteMember(1L);
}
jpa 와 mybatis 에 대해 각자 CRUD 를 만들어봤는데요.
jpa가 확실히 편리하긴 하네요.
복잡한 쿼리를 위할때만 mybatis를 쓰자!!
위의 테스트를 해보니 둘다 제대로 값 나오네요.
잘못된 내용이 있다면 댓글 달아주시길 바랍니다.
감사합니다.