queryFactory
.select(Projections.fields(Person.class,
person.name,
person.age,
Person.Orders, // Person의 Orders객체를 직접 참조, 1(Person) : N(Orders)
Person.Projects // Person의 Projects객체를 직접 참조, 1(Person) : N(Projects)
))
.from(person)
.where(person.age.gt(18))
.fetch();
Person
와 Orders
, Person
와 Projects
간의 관계가 명확하게 드러난다. 이는 코드의 가독성을 높이고, 엔티티 간 관계를 이해하기 쉽게 만들어 줍니다.Person
entity에서 Orders
와 Projects
같은 1:N 관계 데이터를 직접 참조하면, 대량의 데이터를 불러와야 해서 메모리 사용, 처리 시간, 네트워크 부하가 증가할 수 있다.@Entity
@Table(name = "categorys")
public class Category {
@Id
@GeneratedValue
private Long id;
private String name;
private String description1;
private String description2;
private String description3;
@OneToMany(mappedBy = "category", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
private List<Product> products = new ArrayList<>();
}
@Entity
@Table(name = "products")
public class Product {
@Id
@GeneratedValue
private Long id;
private String name;
private Double price;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "category_id")
private Category category;
}
Category
는 여러 Product
를 가질 수 있습니다.@NoArgsConstructor
public class ProductDetails1 {
private Long id;
private String name;
private Double price;
private Category category; // Category 객체 전체 다 포함
}
@Test
@DisplayName("Product 안에 category 데이터 다 가져오기")
public void test1() {
em.flush();
em.clear();
System.out.println("------------ 영속성 컨텍스트 비우기 -----------\n");
JPAQueryFactory queryFactory = new JPAQueryFactory(em);
queryFactory
.select(Projections.fields(ProductDetails1.class,
product.id.as("productId"),
product.name.as("productName"),
product.price.as("productPrice"),
product.category.as("category")))
.from(product)
.where(product.price.gt(0))
.orderBy(product.name.asc())
.fetch();
}
Hibernate:
select
product0_.id as col_0_0_,
product0_.name as col_1_0_,
product0_.price as col_2_0_,
product0_.category_id as col_3_0_,
category1_.id as id1_1_,
category1_.description1 as descript2_1_,
category1_.description2 as descript3_1_,
category1_.description3 as descript4_1_,
category1_.name as name5_1_
from
products product0_
inner join
categorys category1_
on product0_.category_id=category1_.id
where
product0_.price>?
order by
product0_.name asc
@NoArgsConstructor
public class ProductDetails2 {
private Long productId;
private String productName;
private Double productPrice;
private Long categoryId; // Category 객체의 ID만 포함
}
@Test
@DisplayName("Product 안에 category Id 만 가져오기")
public void test2() {
em.flush();
em.clear();
System.out.println("------------ 영속성 컨텍스트 비우기 -----------\n");
JPAQueryFactory queryFactory = new JPAQueryFactory(em);
QProduct product = QProduct.product;
QCategory category = QCategory.category;
queryFactory
.select(Projections.fields(ProductDetails2.class,
product.id.as("productId"),
product.name.as("productName"),
product.price.as("productPrice"),
product.category.id.as("categoryId")))
.from(product)
.where(product.price.gt(0))
.orderBy(product.name.asc())
.fetch();
}
Hibernate:
select
product0_.category_id as col_0_0_,
product0_.name as col_1_0_,
product0_.price as col_2_0_
from
products product0_
where
product0_.price>?
order by
product0_.name asc
@Test
@DisplayName("Product 안에 category Id 만 가져오기 with Pagination")
public void test5() {
em.flush();
em.clear();
System.out.println("------------ 영속성 컨텍스트 비우기 -----------\n");
JPAQueryFactory queryFactory = new JPAQueryFactory(em);
QProduct product = QProduct.product;
int pageNumber = 1;
int pageSize = 20;
long offset = (pageNumber - 1) * pageSize;
List<ProductDetails2> results = queryFactory
.select(Projections.fields(ProductDetails2.class,
product.id.as("productId"),
product.name.as("productName"),
product.price.as("productPrice"),
product.category.id.as("categoryId")))
.from(product)
.where(product.price.gt(0))
.orderBy(product.name.asc())
.offset(offset)
.limit(pageSize)
.fetch();
}
pageNumber
→ 페이지 번호이다.pageSize
→ 페이지 당 항목 수이다.offset
→ 페이지 시작 위치 계산이다.Hibernate:
select
product0_.id as col_0_0_,
product0_.name as col_1_0_,
product0_.price as col_2_0_,
product0_.category_id as col_3_0_
from
products product0_
where
product0_.price>?
order by
product0_.name asc limit ?
@Test
@DisplayName("Product 안에 category Id 만 가져오기 with Streaming")
public void test4() {
em.flush();
em.clear();
System.out.println("------------ 영속성 컨텍스트 비우기 -----------\n");
JPAQueryFactory queryFactory = new JPAQueryFactory(em);
QProduct product = QProduct.product;
try (Stream<ProductDetails2> stream = queryFactory
.select(Projections.fields(ProductDetails2.class,
product.id.as("productId"),
product.name.as("productName"),
product.price.as("productPrice"),
product.category.id.as("categoryId")))
.from(product)
.where(product.price.gt(0))
.orderBy(product.name.asc())
.stream()) {
stream.forEach(productDetails2 -> {
// 각 Product 객체에 대한 처리
System.out.println(productDetails2.getProductName());
});
}
}
queryFactory.stream()
메소드를 사용하여 데이터베이스 결과를 Stream<ProductDetails2>
로 변환한다.try-with-resources
구문을 사용하여 스트림을 자동으로 닫아주므로, 메모리 누수를 방지할 수 있습니다.forEach
메소드를 사용하여 각 요소에 대한 처리를 할 수 있습니다. 이 예제에서는 각 ProductDetails2
객체의 productName
을 출력합니다.Hibernate:
select
product0_.id as col_0_0_,
product0_.name as col_1_0_,
product0_.price as col_2_0_,
product0_.category_id as col_3_0_
from
products product0_
where
product0_.price>?
order by
product0_.name asc