QueryDSL์ด๋?
Querydsl ์ ์ ํ์
์ ์ด์ฉํด์ SQL๊ณผ ๊ฐ์ ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ ์ ์๋๋ก ํด ์ฃผ๋ ํ๋ ์์ํฌ๋ค.
Querydsl - ๋ ํผ๋ฐ์ค ๋ฌธ์
QueryDSL ์ ์ฌ์ฉํ ๊น?
Querydsl์ ํ์
์ ์์ ํ ๋ฐฉ์์ผ๋ก HQL ์ฟผ๋ฆฌ๋ฅผ ์คํํ๊ธฐ ์ํ ๋ชฉ์ ์ผ๋ก ๋ง๋ค์ด์ก๋ค. HQL ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ๋ค๋ณด๋ฉด String ์ฐ๊ฒฐ์ ์ด์ฉํ๊ฒ ๋๊ณ , ์ด๋ ๊ฒฐ๊ณผ์ ์ผ๋ก ์ฝ๊ธฐ ์ด๋ ค์ด ์ฝ๋๋ฅผ ๋ง๋๋ ๋ฌธ์ ๋ฅผ ์ผ๊ธฐํ๋ค. String์ ์ด์ฉํด์ ๋๋ฉ์ธ ํ์
๊ณผ ํ๋กํผํฐ๋ฅผ ์ฐธ์กฐํ๋ค๋ณด๋ฉด ์คํ ๋ฑ์ผ๋ก ์๋ชป๋ ์ฐธ์กฐ๋ฅผ ํ๊ฒ ๋ ์ ์์ผ๋ฉฐ, ์ด๋ String์ ์ด์ฉํด์ HQL ์์ฑํ ๋ ๋ฐ์ํ๋ ๋ ๋ค๋ฅธ ๋ฌธ์ ๋ค.
ํ์
์ ์์ ํ๋๋ก ๋๋ฉ์ธ ๋ชจ๋ธ์ ๋ณ๊ฒฝํ๋ฉด ์ํํธ์จ์ด ๊ฐ๋ฐ์์ ํฐ ์ด๋์ ์ป๊ฒ ๋๋ค. ๋๋ฉ์ธ์ ๋ณ๊ฒฝ์ด ์ง์ ์ ์ผ๋ก ์ฟผ๋ฆฌ์ ๋ฐ์๋๊ณ , ์ฟผ๋ฆฌ ์์ฑ ๊ณผ์ ์์ ์ฝ๋ ์๋์์ฑ ๊ธฐ๋ฅ์ ์ฌ์ฉํจ์ผ๋ก์จ ์ฟผ๋ฆฌ๋ฅผ ๋ ๋น ๋ฅด๊ณ ์์ ํ๊ฒ ๋ง๋ค ์ ์๊ฒ ๋๋ค
Querydsl - ๋ ํผ๋ฐ์ค ๋ฌธ์
QueryDSL ์ฌ์ฉ
gradle ์ค์
plugins {
// ...
id "com.ewerk.gradle.plugins.querydsl" version "1.0.10" // ์ถ๊ฐ
// ...
}
// ...
dependencies {
// ...
implementation 'com.querydsl:querydsl-jpa' // ์ถ๊ฐ
// ...
}
// ...
// queryDSL์ด ์์ฑํ๋ QClass ๊ฒฝ๋ก ์ค์
def querydslDir = "$buildDir/generated/querydsl"
querydsl {
jpa = true
querydslSourcesDir = querydslDir
}
sourceSets {
main.java.srcDir querydslDir
}
configurations {
querydsl.extendsFrom compileClasspath
}
compileQuerydsl {
options.annotationProcessorPath = configurations.querydsl
}
Qํด๋์ค ๋ง๋ค๊ธฐ
gradle ์ค์ ์ด ๋๋ฌ์ผ๋ฉด Qํด๋์ค๋ฅผ ๋ง๋ค์ด๋ณด์.
๋ง๋๋ ๋ฐฉ๋ฒ์ ์์ ๊ทธ๋ฆผ๊ณผ ๊ฐ์ด ๋จผ์ Gradle Project(View โ Tool Windows โ Gradle Project)๋ฅผ ์ด๊ณ Tasks โ other โ compileJava๋ฅผ ์คํ์ํค๋ฉด build โ generated์ Qํด๋์ค๊ฐ ์์ฑ๋๋ค.
Config ์ค์
@Configuration
public class QueryDSLConfig {
@PersistenceContext
private EntityManager entityManager;
@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(entityManager);
}
}
์ด์ JPAQueryFactory๋ฅผ ์ฃผ์
๋ฐ์ QueryDSL์ ์ฌ์ฉํ ์ ์๋ค.
์ฌ์ฉ๋ฒ
1. ๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ
Post ์ํฐํฐ
@Entity
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String title;
@Column(nullable = false)
private String content;
// ...
}
PostRepository
public interface PostRepository extends JpaRepository<Post, Long> {
}
PostRepositorySupport
@Repository
public class PostRepositorySupport extends QuerydslRepositorySupport {
private final JPAQueryFactory jpaQueryFactory;
public PostRepositorySupport(final JPAQueryFactory jpaQueryFactory) {
super(Post.class);
this.jpaQueryFactory = jpaQueryFactory;
}
public List<Post> findByTitle(final String title) {
return jpaQueryFactory.selectFrom(post)
.where(post.title.eq(title))
.fetch();
}
}
selectFrom
์ ์๋ post
๋ ์ด๋์ ์จ ๊ฒ์ผ๊น? ์๊น compileJava๋ฅผ ์คํ์์ผ์ ๋ง๋ Qํด๋์ค์์ ์จ ๊ฒ์ด๋ค.
์ด์ ํ
์คํธ๋ฅผ ํด๋ณด์.
@Test
void findByTitle() {
postRepository.saveAll(Arrays.asList(
new Post("test", "content"),
new Post("test", "content"),
new Post("test", "content"),
new Post("title1", "content"),
new Post("title2", "content"),
new Post("title3", "content")
));
final List<Post> posts = postRepositorySupport.findByTitle("test");
assertAll(
() -> assertThat(posts).hasSize(3),
() -> assertThat(posts.get(0).getTitle()).isEqualTo("test")
);
}
2. Spring Data Jpa Custom Repository ์ ์ฉ
์์ ๊ฐ์ด ์ฌ์ฉํ๋ฉด ํญ์ 2๊ฐ์ Repository(QueryDSL์ Custom Repository, JpaRepository๋ฅผ ์์ํ Repository)๋ฅผ ์์กด์ฑ์ผ๋ก ๋ฐ์์ผ ํ๋ค.
์ด๋ฒ์๋ Custom Repository๋ฅผ JpaRepository ์์ ํด๋์ค์์ ์ฌ์ฉํด๋ณด์.
CustomizedPostRepository
public interface CustomizedPostRepository {
List<Post> findByTitle(final String title);
}
CustomizedPostRepositoryImpl
public class CustomizedPostRepositoryImpl implements CustomizedPostRepository {
private final JPAQueryFactory jpaQueryFactory;
private CustomizedPostRepositoryImpl(final JPAQueryFactory jpaQueryFactory) {
this.jpaQueryFactory = jpaQueryFactory;
}
@Override
public List<Post> findByTitle(final String title) {
return jpaQueryFactory.selectFrom(post)
.where(post.title.eq(title))
.fetch();
}
}
PostRepository
public interface PostRepository extends JpaRepository<Post, Long>, CustomizedPostRepository {
}
์ด๋ ๊ฒ ๊ตฌ์ฑํ๋ฉด CustomizedPostRepositoryImpl
์ ์ฝ๋๋ฅผ ์ฌ์ฉํ ์ ์๋ค. PostRepository
๋ ์ด๋ป๊ฒ CustomizedPostRepository
์ ์์๋ฐ์์ CustomizedPostRepositoryImpl
์ ์ฝ๋๋ฅผ ์ฌ์ฉํ ์ ์์๊น?
Spring ๊ณต์ ๋ฌธ์๋ฅผ ๋ณด์. ์์ฝํ๋ฉด CustomizedRepository
์ธํฐํ์ด์ค๋ฅผ ์์ํ Impl
ํด๋์ค์ ์ฝ๋๋ฅผ ๋น์ ์ Repository
์ CustomizedRepository
๋ฅผ ์์๋ฐ์ ์ฌ์ฉํ ์ ์๋ค๊ณ ํ๋ค. CustomizedRepository
์ ์ด๋ฆ์ ํ ๋ฒ ๋ฐ๊ฟ๋ณด์์ง๋ง ์ ๋์ํ๋ค. ์ค์ํ ๊ฒ์ Impl
์ ๋ฏธ์ฌ ๊ฐ๋ค.
The most important part of the class name that corresponds to the fragment interface is the Impl
postfix.
Spring ๊ณต์ ๋ฌธ์
์ด์ ํ
์คํธํด๋ณด์.
@Test
void findByTitle() {
postRepository.saveAll(Arrays.asList(
new Post("test", "content"),
new Post("test", "content"),
new Post("test", "content"),
new Post("title1", "content"),
new Post("title2", "content"),
new Post("title3", "content")
));
final List<Post> posts = postRepository.findByTitle("test");
assertAll(
() -> assertThat(posts).hasSize(3),
() -> assertThat(posts.get(0).getTitle()).isEqualTo("test")
);
}
3. ์์/๊ตฌํ ์๋ Repository
QueryDSL๋ง์ผ๋ก Repository๋ฅผ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ด๋ค.
PostQueryRepository
@Repository
public class PostQueryRepository {
private final JPAQueryFactory jpaQueryFactory;
public PostQueryRepository(final JPAQueryFactory jpaQueryFactory) {
this.jpaQueryFactory = jpaQueryFactory;
}
public List<Post> findByTitle(final String title) {
return jpaQueryFactory.selectFrom(post)
.where(post.title.eq(title))
.fetch();
}
}
ํ
์คํธํด๋ณด์.
@Test
void findByTitle() {
postRepository.saveAll(Arrays.asList(
new Post("test", "content"),
new Post("test", "content"),
new Post("test", "content"),
new Post("title1", "content"),
new Post("title2", "content"),
new Post("title3", "content")
));
final List<Post> posts = postQueryRepository.findByTitle("test");
assertAll(
() -> assertThat(posts).hasSize(3),
() -> assertThat(posts.get(0).getTitle()).isEqualTo("test")
);
}