build.gradle 설정
plugin 적용
plugins {
id "com.ewerk.gradle.plugins.querydsl" version "1.0.10"
}
dependencies 추가
compile("com.querydsl:querydsl-jpa:4.4.0")
compile("com.querydsl:querydsl-apt:4.4.0")
generate 경로 설정
def generatedSourcesDir = file("${buildDir}/generated/querydsl")
compileQuerydsl {
options.annotationProcessorPath = configurations.querydsl
}
configurations {
querydsl.extendsFrom compileClasspath
}
sourceSets {
main {
java {
srcDir generatedSourcesDir
}
}
}
querydsl {
querydslSourcesDir = relativePath(generatedSourcesDir)
library = "com.querydsl:querydsl-apt"
jpa = true
}
QueryDSL 사용 및 테스트 예제
테스트를 위해 Product, ProductItem 테이블을 생성해서 예제를 진행한다.
CREATE TABLE `product` (
`id` bigint NOT NULL AUTO_INCREMENT,
`contents` varchar(255) DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
`hide` bit(1) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
create table product_item
(
id bigint auto_increment
primary key,
product_id bigint not null,
name varchar(255) not null,
updated_at datetime not null,
created_at datetime not null,
constraint product_item_fk_01
foreign key (product_id) references product (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
INSERT INTO product (contents, created_at, hide, name, updated_at) values ('Test Data1', now(), false, '111', now());
INSERT INTO product (contents, created_at, hide, name, updated_at) values ('Test Data2', now(), false, '222', now());
insert into product_item (product_id, name, updated_at, created_at) value (1, 'productItemName1', now(), now());
insert into product_item (product_id, name, updated_at, created_at) value (1, 'productItemName2', now(), now());
insert into product_item (product_id, name, updated_at, created_at) value (2, 'productItemName3', now(), now());
insert into product_item (product_id, name, updated_at, created_at) value (2, 'productItemName4', now(), now());
Entity, Repository 생성
Product, ProductItem 클래스 생성 후 JPA Repository 를 생성해준다.
QueryDSL 버전이 변경 되면서 Join 관계를 설정하지 않아도 Join문 작성이 가능한데 테스트 해보기 위해 아래 샘플 Entity 에서는 Join관계를 설정하지 않았다.
Product
import javax.persistence.*;
import java.time.LocalDateTime;
@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String contents;
private Boolean hide;
@Column(name = "created_at", nullable = false)
private LocalDateTime createdAt;
@Column(name = "updated_at", nullable = false)
private LocalDateTime updatedAt;
}
ProductItem
import javax.persistence.*;
import java.time.LocalDateTime;
@Entity
public class ProductItem {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "product_id", nullable = false)
private Long productId;
@Column(name = "name")
private String name;
@Column(name = "created_at", nullable = false)
private LocalDateTime createdAt;
@Column(name = "updated_at", nullable = false)
private LocalDateTime updatedAt;
}
ProductRepository
import com.xxx.boot.base.model.Product;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
}
ProductItemRepository
import com.xxx.boot.base.model.ProductItem;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ProductItemRepository extends JpaRepository<ProductItem, Long> {
}
Q클래스 생성 확인
Entity 생성 후 gradle > build 를 실행해보면 buildDir 경로 밑에 Q클래스 들이 생성된 것이 확인된다.
QueryDSL Repository, Select DTO 생성
QueryDSL에서 Join 결과를 ProductDetail 클래스를 사용하여 select를 할것 이다.
public class ProductDetail {
private Long id;
private String name;
private String content;
private String itemName;
public ProductDetail(Long id, String name, String content, String itemName) {
this.id = id;
this.name = name;
this.content = content;
this.itemName = itemName;
}
}
Custom Repository 를 JpaRepository 상속한 클래스에서 사용할 수 있는 기능이 있는데 이를 활용한다.
Spring Data 공식 문서에 자세한 설명이 있다.
import com.xxx.boot.base.model.ProductDetail;
import java.util.List;
public interface ProductRepositoryCustom {
List<ProductDetail> findAllProductDetail();
}
Projections.constructor 를 사용하면 join 결과에서 특정 필드만 사용하여 클래스에 매핑시킬수 있다. 매핑시 ProductDetail 클래스의 필드 순서와 Select 필드 순서를 맞춰주어야 한다.
import java.util.List;
import static com.xxx.boot.base.model.QProduct.product;
import static com.xxx.boot.base.model.QProductItem.productItem;
public class ProductRepositoryImpl extends QuerydslRepositorySupport implements ProductRepositoryCustom {
public ProductRepositoryImpl() {
super(Product.class);
}
@Override
public List<ProductDetail> findAllProductDetail() {
return from(product)
.leftJoin(productItem).on(product.id.eq(productItem.productId))
.select(getProductDetailProjections())
.fetch();
}
public ConstructorExpression<ProductDetail> getProductDetailProjections() {
return Projections.constructor(ProductDetail.class,
product.id, product.name, product.contents, productItem.name);
}
}
위에서 작성한 ProductRepository 클래스를 ProductRepositoryCustom 상속 받도록 추가해준다.
import com.xxx.boot.base.model.Product;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ProductRepository extends JpaRepository<Product, Long>, ProductRepositoryCustom {
}
Test 코드
import com.xxx.boot.base.model.Product;
import com.xxx.boot.base.model.ProductDetail;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest
public class ProductRepositoryTest {
@Autowired
private ProductRepository productRepository;
@Test
public void productFindTest() {
List<Product> productList = productRepository.findAll();
assertThat(productList.size()).isNotNull();
}
@Test
public void findAllProductDetailTest() {
List<ProductDetail> productDetails = productRepository.findAllProductDetail();
assertThat(productDetails.size()).isNotNull();
}
}
실행 결과
'Develop > java,spring' 카테고리의 다른 글
JPA 순환 참조 해결 방법 (0) | 2020.11.17 |
---|---|
Intellij 단축키 (0) | 2020.11.12 |
Junit5 가이드 (0) | 2020.11.05 |
application.properties 설정 목록 (0) | 2020.11.04 |
MSA(Micro Service architecture) (0) | 2020.11.04 |