월 17,600원
5개월 할부 시다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 미해결실전! Querydsl
페이징 활용 1
pagable.getOffset이 없습니ㅏㄷ.
- 미해결실전! Querydsl
QueryDSL 에서 leftJoin & fetchJoin 후 lazy loading 이 되는 현상
영한님 안녕하세요, QueryDSL 공부 중 막히는 부분이 있어 질문드립니다.1:N 연관관계를 가지고 있는 두 엔티티 Team 과 Member 가 있을 때, QueryDSL 로 leftJoin & fetchJoin 으로 두 테이블을 조인하여 Member 목록을 조회하고 싶은데, 만약 Team 테이블에 FK 에 해당하는 row 가 존재하지 않는 경우에는 Member.team 에 그냥 null 이 들어있고 객체에 접근하더라도 추가적인 select 쿼리가 실행되지 않도록 하고 싶습니다. 그런데 제 바람과는 달리 Member.team 을 참조하는 시점에 lazy loading 이 되면서 select 쿼리가 실행되더라고요.실제 코드를 바탕으로 설명해보겠습니다.아래와 같이 1:N 연관 관계를 갖는 Team 과 Member 라는 엔티티가 있습니다.@Table(name = "member") @Entity class Member( @Id @GeneratedValue(strategy = GenerationType.IDENTITY) val id: Int = 0, @Column(name = "team_id") var teamId: Long? = null, @Column(name = "name") var name: String? = null, @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "team_id", insertable = false, updatable = false, foreignKey = ForeignKey(name = "none")) val team: Team? = null, ) @Table(name = "team") @Entity class Team( @Id @GeneratedValue(strategy = GenerationType.IDENTITY) val id: Long = 0, @Column(name = "name") var name: String? = null, )여기서 아래의 코드로 left outer join 쿼리를 실행합니다.val members = from(member) .leftJoin(member.team, team).fetchJoin() .fetch()DB 는 아래와 같이 데이터가 저장되어 있습니다.// team +----+-------+ | id | name | +----+-------+ | 1 | team1 | +----+-------+ // member +----+---------+------+ | id | team_id | name | +----+---------+------+ | 1 | 2 | John | +----+---------+------+그럼 저는 아래와 같은 Member 객체 하나로만 이루어진 List 를 얻을 수 있을 거라고 생각했고, team 변수에 접근할 때 select 쿼리 실행 없이 null 만을 반환할 것이라고 기대했습니다.{ "id": 1, "team_id": 2, "name": "John", "team": null }하지만 아래와같이 member 테이블을 lazy loading 하는 로그가 찍히네요.Hibernate: insert into team (id, name) values (default, ?) Hibernate: insert into member (id, name, team_id) values (default, ?, ?) Hibernate: select member0_.id as id1_7_0_, team1_.id as id1_9_1_, member0_.name as name2_7_0_, member0_.team_id as team_id3_7_0_, team1_.name as name2_9_1_ from member member0_ left outer join team team1_ on member0_.team_id=team1_.id Hibernate: select team0_.id as id1_9_0_, team0_.name as name2_9_0_ from team team0_ where team0_.id=?그런데 만약 DB 의 데이터 중 member 의 team_id 만 1로 변경하니 쿼리 후 member.team 에 접근하더라도 아래와 같이 lazy loading 하는 로그가 찍히지 않았습니다.Hibernate: insert into team (id, name) values (default, ?) Hibernate: insert into member (id, name, team_id) values (default, ?, ?) Hibernate: select member0_.id as id1_7_0_, team1_.id as id1_9_1_, member0_.name as name2_7_0_, member0_.team_id as team_id3_7_0_, team1_.name as name2_9_1_ from member member0_ left outer join team team1_ on member0_.team_id=team1_.id테스트에 사용한 코드는 아래와 같습니다.@DataJpaTest @Import(MemberService::class) // MemberService.listMembers() 에서 QueryDsl 로 쿼리를 합니다. class MyTest( private val sut: MemberService, private val em: EntityManager, ) : FunSpec( { beforeEach { val team = Team(name = "team1") em.persist(team) val member = Member( name = "John", teamId = team.id + 1, // 이것만 team.id 로 바꾸면 team 접근 시 select 로그가 찍히지 않습니다. ) em.persist(member) em.clear() } test("my test") { val members = sut.listMembers() members.shouldNotBeEmpty() val team = members.first().team println(team) } }, )어차피 조회한 엔티티에 변경을 가하지는 않을 것이라, Member 엔티티를 detach 시키고 team 에 접근하면 lazy loading 이 안될까 싶어서 해보았는데 여전히 lazy loading 이 되더라구요 ^^;일단 @QueryProjection 을 붙인 별도의 DTO 를 정의해 아래와같이 쿼리하는 식으로 해결하려고 하는데 더 좋은 방법은 없을까요?class MemberDto @QueryProjection constructor( val id: Int, val teamId: Long? = null, val name: String? = null, val teamName: String? = null, ) @Service @Transactional(readOnly = true) class MemberService : QuerydslRepositorySupport(Member::class.java) { private val member = QMember.member private val team = QTeam.team fun listMembers(): List<MemberDto> { val members = from(member) .select(QMemberDto(member.id, member.teamId, member.name, team.name)) .leftJoin(member.team, team) .fetch() return members } }감사합니다.
- 미해결실전! Querydsl
QueryDSL 5.0.0 기준으로 강의 내용을 정리했는데 올바르게 이해한 것일까요?
searchPageSimple(): 조회 쿼리와 카운트 쿼리를 한번에 실행searchPageComplex(): 조회 쿼리와 카운트 쿼리를 분리QueryDSL 5.0.0부터는 fetchResults()와 fetchCount()를 deprecated 메서드로 공지함에 따라, 강의 내용처럼 searchPageSimple()과 searchPageComplex()를 구분해서 구현할 필요 없이 searchPageComplex()의 방식으로만 구현하면 됨.@Override public Page<MemberTeamDto> searchWithPaging(MemberSearchCond cond, Pageable pageable) { // 데이터 조회 쿼리 (페이징 적용) List<MemberTeamDto> content = queryFactory .select( new QMemberTeamDto( member.id.as("memberId"), member.username, member.age, team.id.as("teamId"), team.name.as("teamName") ) ) .from(member) .leftJoin(member.team, team) .where( usernameEq(cond.getUsername()), teamNameEq(cond.getTeamName()), ageGoe(cond.getAgeGoe()), ageLoe(cond.getAgeLoe()) ) .offset(pageable.getOffset()) .limit(pageable.getPageSize()) .fetch(); // count 쿼리 (조건에 부합하는 로우의 총 개수를 얻는 것이기 때문에 페이징 미적용) Long total = queryFactory .select(member.count()) // SQL 상으로는 count(member.id)와 동일 .from(member) .leftJoin(member.team, team) .where( usernameEq(cond.getUsername()), teamNameEq(cond.getTeamName()), ageGoe(cond.getAgeGoe()), ageLoe(cond.getAgeLoe()) ) .fetchOne(); return new PageImpl<>(content, pageable, total); } 이 때, total을 아래와 같이 구할 수도 있지만long total = queryFactory .selectFrom(member) .leftJoin(member.team, team) .where( usernameEq(cond.getUsername()), teamNameEq(cond.getTeamName()), ageGoe(cond.getAgeGoe()), ageLoe(cond.getAgeLoe()) ) .fetch() // 조건에 부합하는 전체 데이터를 조회 (List) .size(); // List의 길이로 total을 구하기count 함수는 SQL 차원에서 지원하기 때문에 굳이 이렇게 전체 데이터를 받아온 뒤에 애플리케이션 레벨에서 별도로 size()를 호출해서 구할 필요 없고, 처음부터 카운트 쿼리를 호출하는 것이 나음.이유는 전체 데이터를 불러오고 나서 size()로 구하는 방식은 영속성 컨텍스트에 데이터를 전부 받아온 뒤에 개수를 따로 세는 것이기 때문에 불필요하게 메모리를 잡아먹기 때문. total을 구하는 방식에 대한 내용은 강의에 언급되지 않았기 때문에 제가 따로 검색해보고 내린 결론입니다. 제가 생각한 것이 맞는지 궁금합니다.이외에도 잘못된 부분이 있다면 지적해주시면 감사하겠습니다.
- 미해결실전! Querydsl
Projections.fields 관련 질문입니다.
Projections을 이용한 조회 테스트를 하고 있던중Projections.fields에 대해 궁금한점이 있습니다. Dto에 @AllArgsConstructor만 적용하고 Projections.fields를 사용하게 되면 해당 인스턴스를 만들지 못한다고 exception이 발생합니다.영한님께서 강의에서 말쓴하신 Projections.fields의 특성은 getter, setter가 필요가 없고 생성자 또한 필요가 없다라고 하셨는데 이상하게 @AllArgsConstructor를 추가하면 exception이 발생합니다.ex@Getter @AllArgsConstructor public class ArticleFieldsDto{ private Long id; private String title; private LocalDateTime lastModifiedDate; private Long lastModifiedBy; } 예외 로그com.querydsl.core.types.ExpressionException: com.mylaboratory.jpa_and_querydsl.projections.dto.ArticleFieldsDto at com.querydsl.core.types.QBean.newInstance(QBean.java:246) at com.querydsl.core.support.NumberConversions.newInstance(NumberConversions.java:86) at com.querydsl.jpa.FactoryExpressionTransformer.transformTuple(FactoryExpressionTransformer.java:51) at org.hibernate.sql.results.internal.RowTransformerTupleTransformerAdapter.transformRow(RowTransformerTupleTransformerAdapter.java:30) at org.hibernate.sql.results.internal.StandardRowReader.readRow(StandardRowReader.java:109) at org.hibernate.sql.results.spi.ListResultsConsumer.consume(ListResultsConsumer.java:198) at org.hibernate.sql.results.spi.ListResultsConsumer.consume(ListResultsConsumer.java:33) at org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.doExecuteQuery(JdbcSelectExecutorStandardImpl.java:443) at org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.executeQuery(JdbcSelectExecutorStandardImpl.java:166) at org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.list(JdbcSelectExecutorStandardImpl.java:91) at org.hibernate.sql.exec.spi.JdbcSelectExecutor.list(JdbcSelectExecutor.java:31) at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.lambda$new$0(ConcreteSqmSelectQueryPlan.java:113) at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.withCacheableSqmInterpretation(ConcreteSqmSelectQueryPlan.java:335) at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.performList(ConcreteSqmSelectQueryPlan.java:276) at org.hibernate.query.sqm.internal.QuerySqmImpl.doList(QuerySqmImpl.java:571) at org.hibernate.query.spi.AbstractSelectionQuery.list(AbstractSelectionQuery.java:363) at org.hibernate.query.sqm.internal.QuerySqmImpl.list(QuerySqmImpl.java:1073) at org.hibernate.query.spi.AbstractSelectionQuery.getSingleResult(AbstractSelectionQuery.java:457) at org.hibernate.query.sqm.internal.QuerySqmImpl.getSingleResult(QuerySqmImpl.java:1103) at com.querydsl.jpa.impl.AbstractJPAQuery.getSingleResult(AbstractJPAQuery.java:214) at com.querydsl.jpa.impl.AbstractJPAQuery.fetchOne(AbstractJPAQuery.java:326) at com.mylaboratory.jpa_and_querydsl.projections.ProjectionsTest.projections_field(ProjectionsTest.java:70) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727) at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156) at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:147) at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:86) at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103) at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93) at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106) at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64) at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45) at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37) at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92) at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:217) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:213) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:138) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35) at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57) at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:147) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:127) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:90) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:55) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:102) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:54) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86) at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86) at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53) at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71) at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38) at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11) at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35) at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235) at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54) Caused by: java.lang.InstantiationException: com.mylaboratory.jpa_and_querydsl.projections.dto.ArticleFieldsDto at java.base/java.lang.Class.newInstance(Class.java:639) at com.querydsl.core.types.QBean.create(QBean.java:251) at com.querydsl.core.types.QBean.newInstance(QBean.java:222) ... 91 more Caused by: java.lang.NoSuchMethodException: com.mylaboratory.jpa_and_querydsl.projections.dto.ArticleFieldsDto.<init>() at java.base/java.lang.Class.getConstructor0(Class.java:3585) at java.base/java.lang.Class.newInstance(Class.java:626) ... 93 more 예외가 발생하지 않는 경우는 아래와 같습니다.@NoArgsConstructor, @AllArgsConstructor 모두 사용@NoArgsConstructor, @AllArgsConstructor 모두 사용 X@NoArgsConstructor 만 사용
- 미해결실전! Querydsl
UPDATE 동적쿼리 사용 질문
안녕하세요. 강의 듣고 미니 프로젝트 하며 궁금한 점이 생겨 질문드려봅니다. UPDATE문에서도 동적쿼리를 사용하고 싶어 정적쿼리를 아래와 같이 구현해보았는데 혹시 WHERE절의 BooleanExpression처럼 조금 더 깔끔하게 처리할 수 있는 방법이 있을지 문의드려봅니다. 감사합니다.
- 미해결실전! Querydsl
QueryDsl BooleanExpression에 대한 질문
[질문 내용]안녕하세요, QueryDsl 동적 쿼리 생성 코드 관련 선생님의 의견이 궁금합니다.BooleanExpression을 활용하여 동적 쿼리들의 조건을 생성하다보면 Entity, Column, 조건, 상황 별로 다양하고 많은 케이스들이 생겨 그만큼 코드 수를 많이 차지해 관리가 어려울 것이라 생각이 듭니다.그래서 reflection을 사용하여 아래와 같이 처리를 하면 어떨까 싶습니다.public class BooleanExpressionGenerator<T extends EntityPathBase> { private final T qEntity; public BooleanExpressionGenerator(T qEntity) { this.qEntity = qEntity; } public static <T extends EntityPathBase> BooleanExpressionGenerator<T> of(T QEntity) { return new BooleanExpressionGenerator(QEntity); } public <F extends ComparableExpressionBase> BooleanExpression createExpression(F filed, MethodName methodName, Object arg) throws Exception { Method method = filed.getClass().getMethod(methodName.name, arg.getClass()); return (BooleanExpression) method.invoke(qEntity, arg); } @Getter @RequiredArgsConstructor public enum MethodName { EQ("eq"), NE("ne"), GT("gt"), GTE("goe"), LT("lt"), LTE("loe"); private final String name; } } BooleanExpressionGenerator.of(book).createExpression(name, ComparableExpression.EQ, "자바 ORM 표준 JPA 프로그래밍");사실 질문 작성을 위해 작성하는데 5분정도 걸린 급조 된 코드인 점 먼저 말씀드립니다...!(실제 동작도 하지 않는 더티 코드)단지 이런 접근법에 대해서 선생님은 어떻게 생각하시는지가 궁금합니다.추가로 현재 선생님이 근무하시는 곳에서도 동적 쿼리 관련해서 BooleanExpression을 상당히 많이 활용할 것으로 보여지는데 어떤 식으로 관리하고 처리하는지도 궁금합니다!두서 없이 적은 점 양해부탁드립니다..!감사합니다.
- 미해결실전! Querydsl
서브쿼리 alias orderby 방법이 있나요 ?
안녕하세요 querydsl 코딩하다 막히는부분이 있어 질문글 남깁니다. return applyPagination(pageable, contentQuery -> contentQuery .selectDistinct(new QLocationDetailDto( member.idx.as("idx"), member.nickname.as("nickname"), member.memberImg.as("img"), ExpressionUtils.as( JPAExpressions.selectDistinct(qaComment.qa.idx.count()) .from(qaComment) .join(qaComment.qa, qa) .where(qaComment.member.eq(member)), "answerCount")) ) .from(member) .leftJoin(member.memberAddress, memberAddress) .leftJoin(member.qaComments, qaComment) .where( memberAddress.cityIdx.eq(cityIdx), member.grade.ne(QUIT), member.phoneNum.isNotNull(), memberEqualsRegion(regionIdx) ) .orderBy("answerCount").desc() );orderBy에 서브쿼리 이름명을 넣고싶은데 안되는거같아 문의드립니다 다른방법이 있을까요
- 미해결실전! Querydsl
join()를 작성하지 않아도 자동으로 join을 해주는데 join()을 써주는 이유
join()를 작성하지 않아도 자동으로 join을 해주는데 join()을 써주는 이유가 궁금합니다. @Test public void group() { List<Tuple> result = queryFactory .select(member.team.name, member.age.avg()) .from(member) // .join(member.team, team) .groupBy(member.team.name) .fetch(); Tuple teamA = result.get(0); Tuple teamB = result.get(1); assertThat(teamA.get(member.team.name)).isEqualTo("teamA"); assertThat(teamA.get(member.age.avg())).isEqualTo(15); //10, 20 assertThat(teamB.get(member.team.name)).isEqualTo("teamB"); assertThat(teamB.get(member.age.avg())).isEqualTo(35); //30, 40 }제가 위와 같이 코드를 수정하여 돌려봤는데 테스트를 잘 통과하였고 생성된 sql에서도 join을 자동으로 해주었습니다. 따로 join()을 쓰신 이유가 있을까요?
- 미해결실전! Querydsl
test
test할때는 log.info잘 사용 안하나요?
- 미해결실전! Querydsl
안녕하세요
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]1. start.spring.io 에 Querydsl 을 지원하지 않는 특별한 이유가 있는지 궁금합니다. 그리고 build.gradle 에서 Querydsl 설정이 잘 안된다면 임의로 QHello 파일을 생성하더라도 문제가 없는 것인지 궁금합니다.2. H2 DB 접근 시 localhost 로 변경해서 사용해야 하는 이유가 뭘까요?
- 미해결실전! Querydsl
3.0 querydsl
선생님이 올린 3.0 + querydsl로 다시 gradle했는데 Gradel -> other -> complieQuerydsl이 없는데 상관없는건가요?
- 미해결실전! Querydsl
중첩 객체 조회 관련
안녕하세요! querydsl 강의를 보고 실무에 적용 하던 중 중첩 객체에 대해서 한번의 join query로 만들 수 없을지 궁금하여 질문 드립니다. 만약, A -< B -< C (-< 은 one to many 를 의미) 관계를 가진 객체가 있다고 할 때selectFrom(A) .leftJoin(A.B) .leftJoin(A.B.C) .where(A.id.eq(1)) .fetch();위 코드의 반환 값이 아래와 같기를 희망 하는데요A = { id: 1, Bs: [ { id : 1, Cs : [ { id: 1 } ] } ] }oneToMany를 조인 하게 되면, SQL은 many의 row를 반환하게 되면서, A객체가 B의 갯수만큼 반환 되게 됩니다.group by등을 통해 해결 하려 했을 때는, oneToMany 컬렉션들이 모두 불러와지지 않는것을 확인 하였고, group by를 활용 하지 않고, 모든 row를 불러와서 aggregate하는 방식으로 코드를 구현 했습니다. 매번 쿼리문을 작성할 때 마다 aggregate하는 코드를 작성하는 것은 옳지 않을 것 같아, 혹시 더 나은 방법이 있을지 문의 드립니다. 혹시 중첩 쿼리를 join 으로 모두 찾아 객체에서 맵핑하려는 시도가 bad practice라면 쿼리 숫자가 늘어나더라도, findByAId, findByBId 등의 방식으로 여러번 쿼리를 하고, 중간에 캐시 레이어를 두는것이 더 나을지도 궁금 합니다. 항상 좋은 강의 감사합니다 :)
- 미해결실전! Querydsl
스프링 부트 3.0 + Querydsl 설정 관련
안녕하세요! 강의 잘 듣고 있습니다 감사합니다.강의를 따라가다가 잘 안되는 부분이 있어서 질문글 하나 남겨봅니다.Gradle -> Tasks -> build -> cleanGradle -> Tasks -> other -> compileQuerydsl실행 시에 정상적으로 Q타입 클래스가 생성이 됩니다.그런데 이후에 테스트 코드를 돌려보면이런 오류가 뜹니다.이것저것 해보다가 build파일이 없는 상태에서(삭제 후에) 테스트를 돌리면 돌아가긴 합니다.정상적으로 빌드해서 돌려보고 싶은데 설정을 어떻게 바꿔야 할까요?https://drive.google.com/file/d/1H7Sz9Z994odDceIhuTp9S4bpfvhDjxAr/view?usp=sharing구글 드라이브 링크입니다.
- 미해결실전! Querydsl
update시 JPA와 queryDsl방법 질문드립니다
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/)[질문 내용]안녕하세요 JPA강의와 queryDsl 강의를 모두 들으면서 포폴을 만들던 중 궁금한게 생겨 질문드립니다 . 먼저 질문이 많아서 죄송하고 늘 감사드립니다. 1.질문 jpa에서 update(변경감지를 사용한 방법 )public Team updateTeamInfo(TeamInfoUpdateDto teamInfoUpdateDto) { Team team = em.find(Team.class, teamInfoUpdateDto.getTeamId()); team.setTeamName(teamInfoUpdateDto.getTeamName()); team.setDetailIntro(teamInfoUpdateDto.getDetailIntro()); return team; }2. queryDsl에서 update 방법public long updateTeamInfo(TeamInfoUpdateDto teamInfoUpdateDto) { return queryFactory .update(team) .set(team.teamName, teamInfoUpdateDto.getTeamName()) .where(team.id.eq(teamInfoUpdateDto.getTeamId())) .execute(); }이 두 방법 다 정상적으로 select 쿼리 이후 update쿼리가 나가는것 까지 확인을 했는데, 차이점이라곤 queryDsl의 리턴 값이 long이다 라는 것밖에 확인을 못했습니다. 둘 중 어떤 것이 update를 할 때 사용하는게 좋을지 모르겠어서 질문드립니다.. 그냥 리턴 차이가 전부인지... 2. 질문queryDsl에서 update 쿼리의 리턴 값이 long인데 엔티티 타입으로 리턴할 순 없나요?? 3. 질문2번의 문제로 인해 update쿼리의 겨우 update 로직 이후 리턴 타입은 void로 주고 update한 엔티티를 select해서 엔티티 결과값을 리턴 해주었는데 select 쿼리 한 방 추가로 더 나가긴 하는데 이방법이 괜찮은지 다른 방법이 있는지 궁금합니다.. 감사합니다
- 미해결실전! Querydsl
dto로 바로 가져오는것에대해 궁금한점이 있습니다 .
=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? 네 (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 네 (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? 네 (예/아니오)[질문 내용]강의를 듣기전에는 dto를 select절에서 바로 받는방법이 아닌 , 그대로 뽑아온뒤에 서비스단에서 dto도메인에 만들어둔 생성자펙터리메서드를 사용해서 반환받았습니다 . 리스트 같은경우에는 스트림맵을 사용했었고요 , 혹시 서비스단에서 반환하는방법이 단점이 있어서dto로 바로 받는방법을 사용해야하는건지 , 아니면 그저 선택 ?의 문제인지 알고싶습니다 .
- 미해결실전! Querydsl
join table.as() 테이블 이름을 수정 할 수 있나요 ??
안녕하세요 querydsl 공부중인 김준엽 입니다.chatRoom에 member1 member2 연관 관계가 있다면은 querydsl에 .select(chatRoom) .from(chatRoom) .join(chatRoom.member1, member) .join(chatRoom.member2, member) .fetch(); 있다면 .join(chatRoom.member1, member.as(member1) ) 해서 select에 member1.namemember2.name구분 지어서 표현이 가능한가요 ??
- 미해결실전! Querydsl
Querydsl과 JPQL의 성능차이가 존재하나요?
영한님 안녕하세요!Querydsl 관련해서 몇가지 질문이 있습니다.같이 공부하고 있는 스터디원 분들께서 Querydsl을 사용하면 JPQL에 비해 성능이 좋아진다고 설명해주셨습니다.저는 강의를 듣고 Querydsl을 사용하면 JPQL로 변환되어 쿼리가 나간다고 이해하고 있었습니다. "Querydsl은 결국 JPQL로 변환되어서 DB에 쿼리가 나간다" 가 맞는지 질문드리고 싶습니다.또한 Querydsl의 가장 큰 장점은 컴파일 시점에 에러를 잡을 수 있다는 것이라고 알고 있는데, 성능적인 부분에서의 장점도 있는지 여쭤보고 싶습니다. 감사합니다!!
- 미해결실전! Querydsl
SpringBoot 3.0.2 버전 QueryDSL 설정문제
[질문 내용]안녕하세요. 수강생 김땡땡이라고 합니다.추가로 작성해주신 Spring 3.0 버전 설정을 적용해서 build.gradle을 작성했는데 아래와 같은 에러가 발생하면서 테스트 에러가 발생합니다. 먼저 강의내용과 수업자료를 통해서 Springboot 3.0 이상 버전에서 설정해야되는 부분들을 추가했습니다. javax관련 오류는 뜨지 않습니다. 하지만 다른 문제가 생겼는데요.. gradle >other >compileQuerydsl 을 실행하면, 문제점이 있습니다. compileQuerydsl을 통해서 생성되는 Q Class는 build 경로에만 생기고 src/main에는 생성이 되지 않는다.<build 경로 아래에는 성공적으로 Qclass 가 생성되었습니다.>하지만 src/main아래에는 생성되지 않았네요.. 그래도 build 경로아래에 QClass가 생성되어있으니 간단한 Test는 될 것 같아서 수업 내용을 따라서 작성하고 실행해봤는데. 위와 같은 에러가 발생했습니다. Qclass를 찾지못해 발생하는 오류같은데.. 왜 이런 현상이 생기는지 모르겠습니다. 강사님과 똑같이 작성했는데.. 단순 버전 문제일까요? 이런경우엔 gradle 버전도 상관이 있는건가요? 현재 사용하는 gradle 버전은 확인해보니 7.6입니다. 검색을 많이 해봐도 해결방안이 나오질 않습니다. 도움 부탁드립니다. =======================저의 build.gradle 설정========================plugins { id 'java' id 'org.springframework.boot' version '3.0.2' id 'io.spring.dependency-management' version '1.1.0' id "com.ewerk.gradle.plugins.querydsl" version "1.0.10" } group = 'study' version = '0.0.1-SNAPSHOT' sourceCompatibility = '17' configurations { compileOnly { extendsFrom annotationProcessor } } repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-web' compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.h2database:h2' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' // Querydsl 추가 implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta" annotationProcessor "jakarta.annotation:jakarta.annotation-api" annotationProcessor "jakarta.persistence:jakarta.persistence-api" } tasks.named('test') { useJUnitPlatform() } //querydsl 추가 시작 def querydslDir = "$buildDir/generated/querydsl" querydsl { jpa = true querydslSourcesDir = querydslDir } sourceSets { main.java.srcDir querydslDir } configurations { querydsl.extendsFrom compileClasspath } compileQuerydsl { options.annotationProcessorPath = configurations.querydsl } //querydsl 추가 끝 ======================= Hello Entity =============================package study.querydsl.entity; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; import lombok.Getter; import lombok.Setter; @Entity @Getter @Setter public class Hello { @Id @GeneratedValue private Long id; }
- 미해결실전! Querydsl
@RequestBody, @RequestParam을 사용하면 왜 에러가 뜰까요?
안녕하세요!혼자 복습 해보다가 얼떨결에 시도해본 방법인데, 매개변수에@RequestBody를 선언해주고, 포스트맨에서 Body쪽에 값을 넘겨주었는데 에러가 떴습니다.그리고 @RequestBody를 제거한 뒤에, 포스트맨에서는 Param쪽에 값을 넘겨주니 작동이 됐습니다.전 그동안 연습하면서 controller 쪽에 값을 받아올 때는 @RequestBody / @RequestParam / @PathVariable셋중 하나 반드시 넣어줘야 한다고 배웠었는데요. 강의에서 아무것도 선언해주지 않아야 작동 되는 것을 보고 크게 혼란이 왔습니다.강의의 경우 포스트맨에서 쿼리스트링으로 값을 받아오고 있으니 @RequestParam을 넣어줘야 하지 않나요? 이것도 넣으면 마찬가지로 에러가 뜹니다. 왜 그런건가요??
- 미해결실전! Querydsl
QueryDsl만 이용 VS QueryDsl 과 JPA 기본 제공 api를 혼용
다이나믹한 쿼리가 필요한 부분에서만 QueryDSL를 이용하고, 그 외에는 JPA에서 제공되는 기본 api(save 등)를 이용하고 있습니다.그러다 아래와 같은 고민이 생겼습니다...Q. 향후 어떻게 변할지 모를 "확장성"과 "일관성"을 위해, 처음부터 모든 쿼리를 QueryDsl을 이용하는게 좋을까요...?그게 아니라면, 지금 처럼 JPA에서 제공되는 기본 api와 혼용하며 이용하다가 필요할 때마다, QueryDSL을 이용하여 다이나믹 쿼리를 생성하는게 맞을까요...