JPA를 처음 사용하면서 아직 많이 써보진 않았지만 JPA의 장점과 한계에 느끼고있다.
개인적으로 느끼는 JPA의 가장 큰 장점은 영속성이다.
예시로, Mybatis를 사용할때는 insert 후 바로 컬럼을 업데이트 해줘야 하는일이 있으면 다시 update로직을 불러서 해결했지만
JPA는 쉽게 save(insert, update)후 리턴받은 객체에 다시 set필드만 해줘도 영속성 때문에 적용이 된다는 것이다.
정말 좋은 장점이지만 반대로 단점도 조금 큰 것 같다.
spring-data-jpa에서 제공하는 메서드와 JPQL같은 정적쿼리에는 동적인 쿼리를 만들기에 한계가 있다.
이를 극복하기 위한 방안으로 Querydsl이라는 프레임워크가 있다.
Querydsl
SQL, JPQL을 자바 소스코드로 만들어주는 빌더 API라고 한다.
더 느낌을 주자면, .select().from().where() 이런식으로 쿼리를 자바 코드로 작성할 수 있고, 때문에 동적으로 쿼리 생성이 가능하다. 그래서 많은 사람들이 JPA + Querydsl 조합을 사용한다고 한다.
참고 문서 : https://querydsl.com/static/querydsl/5.0.0/reference/html_single/#jpa_integration
Querydsl 설정
설정은 간단하다. pom에 라이브러리 추가하고 querydsl을 작성할 클래스를 생성해 적용하면 끝이다.
하지만 한 테이블에 여러 Repository를 하는것보다 하나의 Repository에서 관련 메서드를 모두 사용하는게 개인적으로 편해서 기존의 ~~~~Repository 에 추가로 ~~~~RepositoryCustom 인터페이스와 이를 구현한 ~~~~RepositoryCustomImpl 클래스를 만들어서 Service, ServiceImpl처럼 생성할 예정이다.
그 후에 기존 ~~~~Repository에서 ~~~~RepositoryCustom 인터페이스만 상속받으면 된다.
pom.xml
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>4.1.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
<version>4.1.4</version>
</dependency>
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.1.3</version>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>target/generated-sources/java</outputDirectory>
<processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
</plugin>
dependency 2가지와 pulgin 1개를 추가해준다.
플러그인에서는 JPA의 Entity를 어노테이션으로 도메인을 찾아 Querydsl에서 사용하는 Q모델 클래스를 <outputDirectory>에 생성해주는 역할을 한다.
RepositoryCustom, RepositoryCustomImpl 생성
기존에 존재했던 WorkGroupRepository에 추가로 WorkGroupRepositoryCustom과 WorkGroupRepositoryCustomImpl을 생성했다.
package com.poozim.jobcall.repository;
import java.util.List;
import com.poozim.jobcall.model.WorkGroup;
public interface WorkGroupRepositoryCustom{
public List<WorkGroup> getWorkGroupList(int workseq, int memberseq);
public int getWorkGroupMemberCnt(WorkGroup workGroup);
}
package com.poozim.jobcall.repository;
import java.util.List;
import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport;
import org.springframework.stereotype.Repository;
import com.poozim.jobcall.model.QWorkGroup;
import com.poozim.jobcall.model.QWorkGroupMember;
import com.poozim.jobcall.model.WorkGroup;
import com.querydsl.jpa.impl.JPAQueryFactory;
public class WorkGroupRepositoryCustomImpl extends QuerydslRepositorySupport implements WorkGroupRepositoryCustom {
private JPAQueryFactory queryFactory;
public WorkGroupRepositoryCustomImpl(JPAQueryFactory queryFactory) {
super(WorkGroup.class);
this.queryFactory = queryFactory;
}
@Override
public List<WorkGroup> getWorkGroupList(int workseq, int memberseq) {
QWorkGroup workgroup = QWorkGroup.workGroup;
List<WorkGroup> list = queryFactory.selectFrom(workgroup).where(workgroup.work_seq.eq(workseq)).fetch();
return list;
}
@Override
public int getWorkGroupMemberCnt(WorkGroup param) {
QWorkGroupMember wgm = QWorkGroupMember.workGroupMember;
int cnt = (int)queryFactory.selectFrom(wgm).where(wgm.group_seq.eq(param.getSeq())).fetchCount();
return cnt;
}
}
간단하게 select List하는 메서드와 select count 하는 메서드를 만들었다.
selectFrom은 SELECT * FROM을 의미하는 메서드같은데, select() 메서드도 있어서 Q모델의 컬럼을 매개변수로 넣어서 select 할 컬럼을 지정할 수 있다.
where()에는 where(조건1, 조건2 ...)처럼 여러 조건을 걸수 있는데 위처럼 ,만 쓰면 and조건으로 들어간다. 동적으로 조건을 넣어주고 싶을땐 BooleanBuilder를 사용하면 된다.
BooleanBuilder 예시
@Override
public WorkGroup getWorkGroupOne(WorkGroup param) {
QWorkGroup workgroup = QWorkGroup.workGroup;
BooleanBuilder builder = new BooleanBuilder();
if(param.getSeq() > 0){
builder.and(workgroup.seq.eq(param.getSeq()));
}
if(param.getDefaultyn() != null){
builder.and(workgroup.defaultyn.eq(param.getDefaultyn()));
}
if (param.getWork_seq() > 0){
builder.and(workgroup.work_seq.eq(param.getWork_seq()));
}
return queryFactory.selectFrom(workgroup).where(builder).fetchOne();
}
기존 Repository
기존의 WorkGroupRepository에서는 만들어준 custom 인터페이스를 상속받으면 querydsl로 생성한 메서들도 역시 기존의 Repository 메서드로 사용할 수 있다.
참고로 인터페이스는 다중상속 가능, 인터페이스만 상속 가능하다. (Custom, CustomImpl의 2개로 나눈 이유)
결론
Querydsl로 서브쿼리도 가능하고 처음 써봐서 어느정도 낯선것도 있겠지만, 아무리 동적 쿼리라고 해도 동적인건 Mybatis를 따라잡는데 한계가 있다고 느꼈다. 때문에 이후에는 Querydsl은 맛만보고 JPA + Mybatis의 조합으로 구성해 각각의 장점만 이용하는 개발을 해볼 예정이다.
'Spring' 카테고리의 다른 글
Spring OCI(oracle cloud infrastructure) Java SDK 활용 (1) Object Storage bucket 정보 가져오기 (0) | 2021.11.04 |
---|---|
Spring MVC JPA 에서 Mybatis 추가하기 (0) | 2021.10.29 |
Spring 메일 보내기 Mail 보내기 (0) | 2021.10.27 |
Spring JPA @Query 사용하기 JPQL (0) | 2021.10.27 |
Spring Interceptor 적용하기 (0) | 2021.10.25 |