항상 JPA를 사용하면서 JpaRepository와 EntityManager만 사용했었는데 항상 먼가 제한적이다는 느낌을 많이 받았어요.
그래서 Native Query를 어떻게 하면 깔끔하게 코딩할 수 있을까 고민하다가 답을 찾지 못했고 결국 Querydsl이나 jooq로 눈을 돌리게 되었어요.
쿼리의 도움을 받아야 하는 레거시 시스템 환경은 확실히 JPA가 많이 불리한 것 같아요.
개발 환경은 JDK 11 + Spring boot 2.1.9 버전이구요.
예제를 통해 간단히 Querydsl을 설정하고 사용해볼게요.
for Maven
pom.xml
아래 코드 추가 후에 Update Project
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<dependencies>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<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>
</plugins>
</build>
</project>
@Entity를 추가/수정할 때마다 target 폴더에 Q-Class들이 생성/수정됩니다.
for Gradle 5
build.gradle
아래 코드 추가 후에 Refresh Gradle Project
plugins {
id 'com.ewerk.gradle.plugins.querydsl' version '1.0.10'
}
dependencies {
compile 'com.querydsl:querydsl-jpa'
compile 'com.querydsl:querydsl-apt'
}
def querydslDir = "$buildDir/generated/querydsl"
querydsl {
jpa = true
querydslSourcesDir = querydslDir
}
sourceSets {
main.java.srcDir querydslDir
}
configurations {
querydsl.extendsFrom compileClasspath
}
compileQuerydsl {
options.annotationProcessorPath = configurations.querydsl
}
Gradle Tasks - Show All Tasks
Gradle Tasks - compileQuerydsl
compileQuerydsl 하면 @Entity들을 읽어서 build/generated/querydsl 폴더에 Querydsl 사용에 필요한 Q-Class들이 생성되요.
플러그인 설정을 마쳤다면 아주 간단하게 사용해볼게요.
QuerydslConfig
@Configuration
public class QuerydslConfig {
@PersistenceContext
private EntityManager em;
@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(em);
}
}
UserRepositoryCustom
생성할 쿼리의 인터페이스에요.
public interface UserRepositoryCustom {
long updateFailCnt(String email);
}
UserRepositoryImpl
Custom 인터페이스의 비즈니스 로직을 구현해주세요.
이름명은 꼭 Impl를 뒤에 붙여주어야 오류가 나지 않아요.
저는 사용자가 로그인에 실패하면 fain_cnt 컬럼을 +1해주는 쿼리를 만들었어요.
설정이 목표이니 querydsl 문법은 여기서 설명하지 않을게요.
public class UserRepositoryImpl extends QuerydslRepositorySupport implements UserRepositoryCustom {
private final JPAQueryFactory query;
private static QUser user = QUser.user;
public UserRepositoryImpl(JPAQueryFactory queryFactory) {
super(User.class);
this.query = queryFactory;
}
@Override
public long updateFailCnt(String email) {
return query.update(user)
.set(user.failCnt, user.failCnt.add(1))
.where(user.email.eq(email))
.execute();
}
}
UserRepository
커스텀한 인터페이스를 UserRepository에서 상속받아주세요.
public interface UserRepository extends JpaRepository<User, Long>, UserRepositoryCustom {
}
UserService
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
}
userRepository 인스턴스를 통하여
JpaRepository에서 제공하는 메소드 뿐만 아니라 커스텀하게 생성한 쿼리도 함께 사용할 수 있습니다.
장점
- 엔티티 스키마로 만든 Q-Class 기반으로 쿼리를 작성하므로 엔티티 변경 시에 유지보수 매우 유리
- 코드 기반이라 익숙해지기만 하면 자동완성을 통하여 정확하고 빠르게 쿼리문을 작성할 수 있음
- 컴파일 시점에 SQL 에러 잡아줌 (코드 기반이라)
단점
- 굉장히 길고 복잡한 쿼리를 Querydsl로 작성하면 유지보수가 거의 불가한 수준의 쿼리를 많이 보게 됨
- sql 외의 Querydsl도 공부해야 함
- 이건 쓰는 사람에 따라 다른데 결국 프로젝트 소스에 Querydsl을 도입한 순간부터 Querydsl로 도배되기 시작하여 JPA 쓰는 의미를 잃어버리는 경우도 생김 (이건 주의하자는 측면에서 적은 개인적인 단점..?)
'Backend > Spring' 카테고리의 다른 글
Spring Boot 내장 WAS 종류와 특징 (4) | 2019.11.29 |
---|---|
Spring Boot Validation Custom (ConstraintValidator) (0) | 2019.11.19 |
JPA 작성자, 수정자 자동 세팅 Auditor (0) | 2019.11.11 |
JPA @ManyToOne Join (Only JpaRepository) (0) | 2019.11.11 |
Spring Boot Interceptor 적용 (0) | 2019.10.16 |