๋ฌธ์ ์
poi_basic ํ ์ด๋ธ ๋ง์ ์กฐํํ๋ คํ๋๋ฐ, ์๋์ ๊ฐ์ด N + 1 ํ์์ด ์ผ์ด๋๊ณ ์์๋ค.
ํ์ํฉ
- poi_basic ํ ์ด๋ธ๊ณผ poi_facility ํ ์ด๋ธ์ OneToOne๊ด๊ณ์ด๋ค.
- Entity๋ก DB ์กฐํ
ํ ์ด๋ธ ์กฐํ ์ฝ๋
๊ณ ์ฐฐ
๊ทธ๋ ๋ค๋ฉด ์ OneToOne๊ด๊ณ์์ N+1 ๋ฌธ์ ๊ฐ ์๊ธฐ๋์ง ์๊ฐํด๋ณด์๋ค.
๋ถ๋ช ์์ชฝ ํ ์ด๋ธ์ ์ง์ฐ๋ก๋ฉ(Lazyloading)์ ์ค์ ํ์๋๋ฐ, ์ง์ฐ๋ก๋ฉ์ด ์ ๋๋ก ์ ์ฉ๋์ง ์์๋ ๊ฒ์ด๋ค.
์ ๊ทธ๋ผ ์ N+1์ด ๋ฐ์ํ๋์ง ์์๋ณด์.
N+1์ด ๋ฐ์ํ๋ ์ด์ ๋ ํ๋ก์ ์ด๊ธฐํ๊ฐ ์ผ์ด๋ฌ๊ธฐ ๋๋ฌธ์ด๋ค.
์๋ฅผ๋ค์ด, ์๋์ ๊ฐ์ ํ ์ด๋ธ ์ฐ๊ด๊ด๊ณ๊ฐ ์๋ค๊ณ ํ์.
Q. [Order_Item]ํ ์ด๋ธ์ ์กฐํํ๋๋ฐ [Order]ํ ์ด๋ธ๊ณผ [Product]๋ฅผ ํญ์ ํจ๊ป ์กฐํํด์ผ ํ๋?
๋ต์ “No”๋ค. ํ ํ ์ด๋ธ์ ์กฐํํ๋๋ฐ, ์ฐ๊ด๊ด๊ณ์ ์๋ ํ ์ด๋ธ ๋ชจ๋๋ฅผ ์กฐํํ๋ค๋ฉด ์ฐ๋ฆฌ๋ JPA๋ฅผ ์ฌ์ฉํ ํ์๊ฐ ์๋ค.(์ฑ๋ฅ์ด์๊ฐ ์ฌ๊ฐํ๊ธฐ๋๋ฌธ)
๊ทธ๋์ ์ด๋ ๋ฑ์ฅํ๋ ๊ฐ๋ ์ด ํ๋ก์์ธ๋ฐ, Order_Item์ product์ order๋ฅผ ํ๋ก์๋ก ๊ฐ์ ธ์ค๋ ๊ฒ์ด๋ค.
JPA์์ ํ๋ก์๋ ์ค์ ์ํฐํฐ ๊ฐ์ฒด ๋์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์กฐํ๋ฅผ ์ง์ฐํ ์ ์๋ ๊ฐ์ง ๊ฐ์ฒด๋ฅผ ์๋ฏธ
ํ๋ก์์ ํน์ง
- ์ค์ ํด๋์ค๋ฅผ ์์๋ฐ์์ ๋ง๋ค์ด์ง
- ์ค์ ํด๋์ค์ ๊ฒ๋ชจ์์ด ๊ฐ๋ค
- ์ฌ์ฉํ๋ ์ ์ฅ์์ ์ง์ง ์ํฐํฐ ๊ฐ์ฒด์ธ์ง, ํ๋ก์ ๊ฐ์ฒด์ธ์ง ๊ตฌ๋ถํ์ง ์๊ณ ์ฌ์ฉํ๋ฉด ๋จ
- ํ๋ก์ ๊ฐ์ฒด๋ ์ค์ ๊ฐ์ฒด์ ์ฐธ์กฐ(target)๋ฅผ ๋ณด๊ด
- ํ๋ก์ ๊ฐ์ฒด๋ฅผ ํธ์ถํ๋ฉด ํ๋ก์ ๊ฐ์ฒด๋ ์ค์ ๊ฐ์ฒด์ ๋ฉ์๋๋ฅผ ํธ์ถ
์ฆ, Order_Item๋ง ์กฐํํ๊ณ ์ถ๋ค๋ฉด Order_Item์ ์ฐ๊ด๋ ์ํฐํฐ์ธ Product์ Order๋ ํ๋ก์๋ก ์กฐํํ๋ฉด ๋๋ค. ์ฌ๊ธฐ์ ํ๋ก์๋ ์ค์ ๊ฐ์ด ๋ค์ด์์ง๋ ์๋ค(๋น๊นกํต)
ํ๋ก์ ์ด๊ธฐํ
ํ๋ก์๋ก ๋ฐ์์์ง๋ง, ์ด๊ฒ์ ์ค์ ์ํฐํฐ์ฒ๋ผ ์ฌ์ฉํ๋ ค๋ฉด ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์กฐํํด์ ์ค์ ์ํฐํฐ ๊ฐ์ฒด๋ฅผ ์์ฑ ํ ํด๋น ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ๊ฒ ๋๋๋ฐ, ์ด๋ฅผ ํ๋ก์ ๊ฐ์ฒด์ ์ด๊ธฐํ๋ผ ํ๋ค.
N+1 ๋ฐ์์ด์
ํ๋ก์๋ฅผ ์ด๊ธฐํ ํ๊ธฐ์ํด ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์กฐํ๋ฅผ ํ๊ฒ ๋๋๋ฐ, ์ด ๋ N+1ํ์์ด ๋ฐ์ํ๋ค.
ํด๊ฒฐ๋ฐฉ๋ฒ
- ํ์น ์กฐ์ธ(fetch join)
- DTO ์กฐํ
ํ์น ์กฐ์ธ (fetch join)
- SQL์์ ์ฌ์ฉํ๋ ์กฐ์ธ์ ์ข ๋ฅ๊ฐ ์๋
- JPQL์์ ์ฑ๋ฅ ์ต์ ํ๋ฅผ ์ํด ์ ๊ณตํด์ฃผ๋ ๊ธฐ๋ฅ
- ์ฐ๊ด๋ Entity๋ Collection์ SQL 1๋ฒ์ ํจ๊ป ์กฐํํ๋ ๊ธฐ๋ฅ
- Inner Join๊ณผ Left Join ๋ชจ๋ ๊ฐ๋ฅ
DTO ์กฐํ
์ด ๋ฐฉ๋ฒ์ ๋ํด์ ์ฐ์ํ ํ์ ์ฝ์ํธ์์ ๋ฐํํ ์ข์ ์๋ฃ๊ฐ ์์ผ๋ ์ฐธ๊ณ ํ์.
์ฐ์ํ ํ์ ๋ค์ Querydsl ์ฌ์ฉ๋ฒ
์ด ๊ธ์ "์ฐ์ํํ ํฌ์ฝ์ํธ2020 ์์ญ์ต๊ฑด์์ Querydsl ์ฌ์ฉํ๊ธฐ" ์ ๋ฐํ์์ด์ ์ด๋์ฑ๋์ ๊ธฐ์ ๋ธ๋ก๊ทธ๋ฅผ ๋ณด๊ณ ์์ฑํ ๊ธ์ ๋๋ค. ๋ชจ๋ ์์ ์ ์ถ๊ฐ๋ก Querydsl ์ฌ์ฉ ๋ฌธ๋ฒ์ https://github.com/Youngerjesus/Q
velog.io
๊ฐ๋จํ๊ฒ ์์ฝํ๋ฉด,
- ์กฐํํ ๋ Entity ๋ณด๋ค๋ Dto๋ฅผ ์ฐ์ ์ ์ผ๋ก ๊ฐ์ ธ์ค๊ธฐ
- + DTO์กฐํํ ๋ ํ์ํ ์นผ๋ผ๋ง ๊ฐ์ ธ์ค๊ธฐ(๋งค๊ฐ๋ณ์๋ก ๋ฐ์ ์นผ๋ผ์ ์ ์ธํ๊ธฐ)
+ DTO์กฐํํ ๋ ํ์ํ ์นผ๋ผ๋ง ๊ฐ์ ธ์ค๊ธฐ(๋งค๊ฐ๋ณ์๋ก ๋ฐ์ ์นผ๋ผ์ ์ ์ธํ๊ธฐ)
- SELECT ์นผ๋ผ์ Entity๋ ์์ ํ๊ธฐ
ํ๋ก์ ํธ์ ์ ์ฉํ๊ธฐ
ํ์ฌ Entity๋ก ์กฐํํ๋ ์ฟผ๋ฆฌ๋ฅผ DTO์กฐํ ๋ฐฉ์์ผ๋ก ๋ณ๊ฒฝํ๋ ค๊ณ ํ๋ค.
์ ์ฉ๊ณผ์ ์ ๋ค์๊ณผ ๊ฐ๋ค.
- ๋ฉ์๋ return๊ฐ์ dto๋ก ๋ณ๊ฒฝํ๋ค.
- ๊ธฐ์กด์ selectFromํด์ select๋ก ๋ณ๊ฒฝ(Projections.fields ์ ์ฉ ⇒ dto๋ก ์กฐํํ๊ธฐ ์ํจ)
- poiBasic๊ฐ์ฒด๋ QPoiBasicํด๋์ค๋ฅผ static importํ ๊ฐ์ฒด์ด๋ค.
- ๊ฒ์์ฟผ๋ฆฌ์ด๊ธฐ ๋๋ฌธ์ null์ฒดํฌ๋ฅผ ํด์ค์ผํ๊ธฐ์, BooleanExpression์ผ๋ก parameter null์ฒดํฌ
- dist๊ฐ์ฒด๋ ๋๋น์ ์ ์ฅ๋ ์๋ ๊ฒฝ๋ ๊ฐ๊ณผ, ํ๋ผ๋ฏธํฐ๋ก ๋ฐ์ ์๋์ ๊ฒฝ๋๋ก MySQL ๋ด์ฅ function์ธ ST_Distance_Sphereํจ์๋ก ๊ฑฐ๋ฆฌ๋ฅผ ๊ตฌํ ๊ฐ์ด๋ค.
- QueryDSL์์ DB function์ ์ฌ์ฉํ๋ ค๋ฉด Expressions.stringTemplate์ ์ฌ์ฉํด์ผํจ.
- QueryDSL์ ANSI ํจ์์ ๊ฒฝ์ฐ ํจ์๋ช ์ด ๋ฑ๋ก๋์ด ์ง์ํ์ง๋ง, ํจ์๋ช ์ด ๋ฑ๋ก๋์ด ์์ง ์์ ๊ฒฝ์ฐ์ ๋ฐ๋ก Dialect๋ฅผ ์์ ํ ํจ์๋ช ์ ๋ฑ๋กํด์ค์ผํ๋ค.
import org.hibernate.dialect.MariaDB103Dialect;
import org.hibernate.dialect.function.StandardSQLFunction;
import org.hibernate.type.StandardBasicTypes;
public class MariaDBDialectCustom extends MariaDB103Dialect {
public MariaDBDialectCustom() {
super();
registerFunction("ST_Distance_Sphere", new StandardSQLFunction("ST_Distance_Sphere", StandardBasicTypes.STRING));
}
}
spring:
jpa:
database-platform: com.humanf.docent_backend.config.MariaDBDialectCustom
- select๊ตฌ๋ฌธ์์ entity๊ฐ์ฒด์ dto๊ฐ์ฒด์ ๋ช
์ด ์ผ์น ํ์ง์์ผ๋ฉด mapping๋์ง์์
⇒ ๋ณ์นญ(as)๋ก ๋งคํ์ํค๊ธฐ!
์ฝ๋ ๊ฒฐ๊ณผ
public List<PoiBasicResponseDto> findAllBySearchType(PoiBasicSearchCondition condition) {
StringTemplate dist = getDist(condition.getLongitude(), condition.getLatitude());
return queryFactory
.select(Projections.fields(PoiBasicResponseDto.class,
poiBasic.id.as("idx_pb"),
poiBasic.poiNm.as("poi_nm"),
poiBasic.barrierFree.as("barrier_free"),
poiBasic.poiAddress.as("poi_address"),
poiBasic.latitude,
poiBasic.longitude,
dist.as("distance"),
poiBasic.poiDesc.as("poi_desc"),
poiBasic.operationtime,
poiBasic.telNo.as("tel_no"),
poiBasic.inouts,
poiBasic.takentimeGeneral.as("takentime_general"),
poiBasic.takentimeBf.as("takentime_bf"),
poiBasic.tourDifficulty.as("tour_difficulty"),
poiBasic.disabledCd.as("disabled_cd"),
poiBasic.chaoticCd.as("chaotic_cd"),
poiBasic.vidUrl.as("vid_url"),
poiBasic.imgFile1.as("img_file1"),
poiBasic.imgFile2.as("img_file2"),
poiBasic.imgFile3.as("img_file3"),
poiBasic.imgFile4.as("img_file4"),
poiBasic.lineCnt.as("line_cnt"),
poiBasic.spotCnt.as("spot_cnt"),
poiBasic.arCnt.as("ar_cnt"),
poiBasic.toiletCnt.as("toilet_cnt"),
poiBasic.disabledtoiletCnt.as("disabledtoilet_cnt"),
poiBasic.parkingCnt.as("parking_cnt"),
poiBasic.disabledparkingCnt.as("disabledparking_cnt"),
poiBasic.createDt.as("create_dt"),
poiBasic.updateDt.as("update_dt"))
)
.from(poiBasic)
.where(
distLoe(dist, condition.getDistanceRange()),
tourDiffEq(condition.getTourDifficulty()),
disabledCdEq(condition.getDisabledCd())
)
.orderBy(dist.asc())
.fetch();
}
private StringTemplate getDist(BigDecimal longitude, BigDecimal latitude) {
if (latitude != null && longitude != null) {
System.out.println("longitude: " + longitude + ", \t" + "latitude: " + latitude + ", \t");
return Expressions.stringTemplate("ST_Distance_Sphere({0},{1})",
Expressions.stringTemplate("POINT({0},{1})", longitude, latitude),
Expressions.stringTemplate("POINT({0},{1})", poiBasic.longitude, poiBasic.latitude)
);
}
return null;
}
private BooleanExpression distLoe(StringTemplate dist, Integer distance) {
return distance != null ? dist.loe(String.valueOf(distance)) : null;
}
private BooleanExpression tourDiffEq(String tourDifficulty) {
return hasText(tourDifficulty) ? poiBasic.tourDifficulty.eq(tourDifficulty) : null;
}
private BooleanExpression disabledCdEq(String disabledCd) {
return hasText(disabledCd) ? poiBasic.disabledCd.eq(disabledCd) : null;
}
'Languages | Frameworks > Spring' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Redis] springboot + Redis cache(์กฐํ) (1) | 2023.11.20 |
---|---|
@ControllerAdvice๋ฅผ ํตํ ์์ธ์ฒ๋ฆฌ ๋ถ๋ฆฌ, ํตํฉํ๊ธฐ (0) | 2022.12.20 |
[IntelliJ] ์์ฃผ์ฐ๋ ๋จ์ถํค ์ ๋ฆฌ (0) | 2022.11.21 |
[Springboot] spring-security ์ ์ฉ๊ธฐ(2) - OAuth2 ๊ตฌ๊ธ ์์ ๋ก๊ทธ์ธ (2) | 2022.11.02 |
@Autowired, @Component, @Service, @Repository ๋ฑ ์คํ๋ง ์ด๋ ธํ ์ด์ ์ ๊ดํด.. (0) | 2022.11.02 |