From dbad8d8f5f5448ce3854c4c7f703c7a04eb098de Mon Sep 17 00:00:00 2001 From: haerong22 Date: Wed, 8 Sep 2021 00:58:07 +0900 Subject: [PATCH] querydsl : projection, dynamic query, bulk update, sql function --- querydsl/db.mv.db | Bin 49152 -> 49152 bytes .../com/example/querydsl/dto/MemberDto.java | 20 ++ .../com/example/querydsl/dto/UserDto.java | 9 + .../example/querydsl/QuerydslBasicTest.java | 284 ++++++++++++++++++ 4 files changed, 313 insertions(+) create mode 100644 querydsl/src/main/java/com/example/querydsl/dto/MemberDto.java create mode 100644 querydsl/src/main/java/com/example/querydsl/dto/UserDto.java diff --git a/querydsl/db.mv.db b/querydsl/db.mv.db index 5b557e6c25a16712a7614fca61eccdabe5f81323..e4925e946c4c66677372b5a3cbc41507a4d76178 100644 GIT binary patch literal 49152 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 literal 49152 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 diff --git a/querydsl/src/main/java/com/example/querydsl/dto/MemberDto.java b/querydsl/src/main/java/com/example/querydsl/dto/MemberDto.java new file mode 100644 index 00000000..355b723b --- /dev/null +++ b/querydsl/src/main/java/com/example/querydsl/dto/MemberDto.java @@ -0,0 +1,20 @@ +package com.example.querydsl.dto; + +import com.querydsl.core.annotations.QueryProjection; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class MemberDto { + + private String username; + private int age; + + @QueryProjection + public MemberDto(String username, int age) { + this.username = username; + this.age = age; + } +} diff --git a/querydsl/src/main/java/com/example/querydsl/dto/UserDto.java b/querydsl/src/main/java/com/example/querydsl/dto/UserDto.java new file mode 100644 index 00000000..c2806bea --- /dev/null +++ b/querydsl/src/main/java/com/example/querydsl/dto/UserDto.java @@ -0,0 +1,9 @@ +package com.example.querydsl.dto; + +import lombok.Data; + +@Data +public class UserDto { + private String name; + private int age; +} diff --git a/querydsl/src/test/java/com/example/querydsl/QuerydslBasicTest.java b/querydsl/src/test/java/com/example/querydsl/QuerydslBasicTest.java index 7f7edfe1..1fd4897c 100644 --- a/querydsl/src/test/java/com/example/querydsl/QuerydslBasicTest.java +++ b/querydsl/src/test/java/com/example/querydsl/QuerydslBasicTest.java @@ -1,11 +1,19 @@ package com.example.querydsl; +import com.example.querydsl.dto.MemberDto; +import com.example.querydsl.dto.QMemberDto; +import com.example.querydsl.dto.UserDto; import com.example.querydsl.entity.Member; import com.example.querydsl.entity.QMember; import com.example.querydsl.entity.QTeam; import com.example.querydsl.entity.Team; +import com.querydsl.core.BooleanBuilder; import com.querydsl.core.QueryResults; import com.querydsl.core.Tuple; +import com.querydsl.core.types.ExpressionUtils; +import com.querydsl.core.types.Predicate; +import com.querydsl.core.types.Projections; +import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.core.types.dsl.CaseBuilder; import com.querydsl.core.types.dsl.Expressions; import com.querydsl.jpa.JPAExpressions; @@ -15,6 +23,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.annotation.Commit; import org.springframework.transaction.annotation.Transactional; import javax.persistence.EntityManager; @@ -448,4 +457,279 @@ public class QuerydslBasicTest { System.out.println("s = " + s); } } + + @Test + void simpleProjection() { + List result = queryFactory + .select(member.username) + .from(member) + .fetch(); + + for (String s : result) { + System.out.println("s = " + s); + } + } + + @Test + void tupleProjection() { + List result = queryFactory + .select(member.username, member.age) + .from(member) + .fetch(); + + for (Tuple tuple : result) { + String username = tuple.get(member.username); + Integer age = tuple.get(member.age); + System.out.println("username = " + username); + System.out.println("age = " + age); + } + } + + @Test + void findDtoByJPQL() { + List result = + em.createQuery("select new com.example.querydsl.dto.MemberDto(m.username, m.age) from Member m", MemberDto.class) + .getResultList(); + + for (MemberDto memberDto : result) { + System.out.println("memberDto = " + memberDto); + } + } + + @Test + void findDtoBySetter() { + List result = queryFactory + .select(Projections.bean(MemberDto.class, + member.username, + member.age)) + .from(member) + .fetch(); + + for (MemberDto memberDto : result) { + System.out.println("memberDto = " + memberDto); + } + } + + @Test + void findDtoByField() { + List result = queryFactory + .select(Projections.fields(MemberDto.class, + member.username, + member.age)) + .from(member) + .fetch(); + + for (MemberDto memberDto : result) { + System.out.println("memberDto = " + memberDto); + } + } + + @Test + void findDtoByConstructor() { + List result = queryFactory + .select(Projections.constructor(MemberDto.class, // 생성자는 타입으로 매칭 이름 달라도 됨 + member.username, + member.age)) + .from(member) + .fetch(); + + for (MemberDto memberDto : result) { + System.out.println("memberDto = " + memberDto); + } + } + + @Test + void findUserDto() { + List result = queryFactory + .select(Projections.fields(UserDto.class, + member.username.as("name"), + member.age)) + .from(member) + .fetch(); + + for (UserDto userDto : result) { + System.out.println("userDto = " + userDto); + } + } + + @Test + void findUserDtoAlias() { + QMember memberSub = new QMember("memberSub"); + List result = queryFactory + .select(Projections.fields(UserDto.class, + member.username.as("name"), + ExpressionUtils.as(JPAExpressions + .select(memberSub.age.max()) + .from(memberSub), "age") + )) + .from(member) + .fetch(); + + for (UserDto userDto : result) { + System.out.println("userDto = " + userDto); + } + } + + @Test + void findDtoByQueryProjection() { + List result = queryFactory + .select(new QMemberDto(member.username, member.age)) + .from(member) + .fetch(); + + for (MemberDto memberDto : result) { + System.out.println("memberDto = " + memberDto); + } + } + + @Test + void dynamicQuery_booleanBuilder() { + String usernameParam = "member1"; + Integer ageParam = 10; + + List result = searchMember1(usernameParam, ageParam); + assertThat(result.size()).isEqualTo(1); + } + + private List searchMember1(String usernameCond, Integer ageCond) { + + BooleanBuilder builder = new BooleanBuilder(); + if (usernameCond != null) { + builder.and(member.username.eq(usernameCond)); + } + if (ageCond != null) { + builder.and(member.age.eq(ageCond)); + } + + return queryFactory + .selectFrom(member) + .where(builder) + .fetch(); + } + + @Test + void dynamicQuery_WhereParam() { + String usernameParam = "member1"; + Integer ageParam = null; + + List result = searchMember2(usernameParam, ageParam); + assertThat(result.size()).isEqualTo(1); + } + + private List searchMember2(String usernameCond, Integer ageCond) { + + return queryFactory + .selectFrom(member) + .where(allEq(usernameCond, ageCond)) + .fetch(); + } + + private BooleanExpression usernameEq(String usernameCond) { + return usernameCond != null ? member.username.eq(usernameCond) : null; + } + + private BooleanExpression ageEq(Integer ageCond) { + return ageCond != null ? member.age.eq(ageCond) : null; + } + + private BooleanExpression allEq(String usernameCond, Integer ageCond) { + return usernameEq(usernameCond).and(ageEq(ageCond)); + } + + @Test + void bulkUpdate() { + + // bulk update 는 영속성 컨텍스트에는 값이 변하지 않고 남아있다. + // 변경하고 DB에서 다시 조회를 하더라도 영속성 컨텍스트에 이미 값이 있기 때문에 + // 영속성 컨텍스트의 값이 우선권을 가진다. + long count = queryFactory + .update(member) + .set(member.username, "비회원") + .where(member.age.lt(28)) + .execute(); + + List result = queryFactory + .selectFrom(member) + .fetch(); + + for (Member member1 : result) { + System.out.println("member1 = " + member1); + } + + // 벌크 연산 후에는 초기화를 하는 것이 좋다. + em.flush(); + em.clear(); + + result = queryFactory + .selectFrom(member) + .fetch(); + + for (Member member1 : result) { + System.out.println("member1 = " + member1); + } + } + + @Test + void bulkAdd() { + long cnt = queryFactory + .update(member) + .set(member.age, member.age.add(1)) + .execute(); + em.flush(); + em.clear(); + List result = queryFactory + .selectFrom(member) + .fetch(); + + for (Member member1 : result) { + System.out.println("member1 = " + member1); + } + } + + @Test + void bulkDelete() { + long cnt = queryFactory + .delete(member) + .where(member.age.gt(18)) + .execute(); + + em.flush(); + em.clear(); + List result = queryFactory + .selectFrom(member) + .fetch(); + + for (Member member1 : result) { + System.out.println("member1 = " + member1); + } + } + + @Test + void sqlFunction() { + List result = queryFactory + .select(Expressions.stringTemplate( + "function('replace', {0}, {1}, {2})", + member.username, "member", "M")) + .from(member) + .fetch(); + + for (String s : result) { + System.out.println("s = " + s); + } + } + + @Test + void sqlFunction2() { + List result = queryFactory + .select(member.username) + .from(member) +// .where(member.username.eq( +// Expressions.stringTemplate("function('lower', {0})", member.username))) + .where(member.username.eq(member.username.lower())) + .fetch(); + + for (String s : result) { + System.out.println("s = " + s); + } + } }