사용자 정의 리포지토리
2022. 7. 26. 21:27
Querydsl
사용자 정의 리포지토리 사용법 1. 사용자 정의 인터페이스 작성 2. 사용자 정의 인터페이스 구현 3. 스프링 데이터 리포지토리에 사용자 정의 인터페이스 상속 사용자 정의 리포지토리 구성 1. 사용자 정의 인터페이스 작성 public interface MemberRepositoryCustom { List search(MemberSearchCondition condition); } 2. 사용자 정의 인터페이스 구현 public class MemberRepositoryImpl implements MemberRepositoryCustom { private final JPAQueryFactory queryFactory; public MemberRepositoryImpl(EntityManager em) { thi..
실무 활용 - 스프링 데이터 JPA와 Querydsl
2022. 7. 26. 16:52
Querydsl
스프링 데이터 JPA 리포지토리로 변경 스프링 데이터 JPA - MemberRepository 생성 public interface MemberRepository extends JpaRepository { List findByUsername(String username); } 스프링 데이터 JPA 테스트 @SpringBootTest @Transactional class MemberRepositoryTest { @Autowired EntityManager em; @Autowired MemberRepository memberRepository; @Test public void basicTest() { Member member = new Member("member1", 10); memberRepository.s..
조회 API 컨트롤러 개발
2022. 7. 26. 16:43
Querydsl
편리한 데이터 확인을 위해 샘플 데이터를 추가하자. 샘플 데이터 추가가 테스트 케이스 실행에 영향을 주지 않도록 다음과 같이 프로파일을 설정하자 프로파일 설정 src/main/resources/application.yml spring: profiles: active: local 테스트는 기존 application.yml을 복사해서 다음 경로로 복사하고, 프로파일을 test로 수정하자 spring: profiles: active: test 이렇게 분리하면 main 소스코드와 테스트 소스 코드 실행시 프로파일을 분리할 수 있다. 샘플 데이터 추가 @Profile("local") @Component @RequiredArgsConstructor public class InitMember { private fin..
동적 쿼리와 성능 최적화 조회 - Where절 파라미터 사용
2022. 7. 26. 16:22
Querydsl
Where절에 파라미터를 사용한 예제 public List search(MemberSearchCondition condition) { return 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(condition.getUsername()), teamNameEq(condition.getTeamName()), ageGoe(condition.getAgeGoe()), ageLoe(condition..
동적 쿼리와 성능 최적화 조회 - Builder 사용
2022. 7. 26. 16:02
Querydsl
MemberTeamDto - 조회 최적화용 DTO 추가 @Datapublic class MemberTeamDto { private Long memberId; private String username; private int age; private Long teamId; private String teamName; @QueryProjection public MemberTeamDto(Long memberId, String username, int age, Long teamId, String teamName) { this.memberId = memberId; this.username = username; this.age = age; this.teamId = teamId; this.teamName = teamN..
실무 활용 - 순수 JPA와 Querydsl
2022. 7. 26. 15:28
Querydsl
순수 JPA 리포지토리와 Querydsl 순수 JPA 리포지토리 @Repository public class MemberJpaRepository { private final EntityManager em; private final JPAQueryFactory queryFactory; public MemberJpaRepository(EntityManager em) { this.em = em; this.queryFactory = new JPAQueryFactory(em); } public void save(Member member) { em.persist(member); } public Optional findById(Long id) { Member findMember = em.find(Member.clas..
SQL function 호출하기
2022. 7. 26. 01:09
Querydsl
SQL function은 JPA와 같이 Dialect에 등록된 내용만 호출할 수 있다. member M으로 변경하는 replace 함수 사용 @Test public 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 public void sqlFunction2() { List result = q..
수정, 삭제 벌크 연산
2022. 7. 26. 00:57
Querydsl
쿼리 한번으로 대량 데이터 수정 @Test public void bulkUpdate() { long count = queryFactory .update(member) .set(member.username, "비회원") .where(member.age.lt(28)) .execute(); } 기존 숫자에 1 더하기 long count = queryFactory .update(member) .set(member.age, member.age.add(1)) .execute(); 곱하기: multiply(x) 쿼리 한번으로 대량 데이터 삭제 long count = queryFactory .delete(member) .where(member.age.gt(18)) .execute(); 주의: JPQL 배치와 마찬가지로..
동적 쿼리 - Where 다중 파라미터 사용
2022. 7. 25. 23:45
Querydsl
동적 쿼리 - Where 다중 파라미터 사용 @Test public void dynamicQuery_WhereParam() { String usernameParam = "member1"; Integer ageParam = 10; List result = searchMember2(usernameParam, ageParam); assertThat(result.size()).isEqualTo(1); } private List searchMember2(String usernameCond, Integer ageCond) { return queryFactory .selectFrom(member) // .where(usernameEq(usernameCond), ageEq(ageCond)) .where(allEq(us..
동적 쿼리 - BooleanBuilder 사용
2022. 7. 25. 23:26
Querydsl
동적 쿼리를 해결하는 두가지 방식 BooleanBuilder Where 다중 파라미터 사용 @Test public 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) { bu..
프로젝션과 결과 반환 - @QueryProjection
2022. 7. 25. 20:51
Querydsl
생성자 + @QueryProjection @Data public class MemberDto { private String username; private int age; public MemberDto() { } @QueryProjection public MemberDto(String username, int age) { this.username = username; this.age = age; } } ./gradlew compileQuerydsl QMemberDto 생성 확인 @QueryProjection 활용 @Test public void findDtoByQueryProjection() { List result = queryFactory .select(new QMemberDto(member.user..
프로젝션과 결과 반환 - DTO 조회
2022. 7. 25. 16:35
Querydsl
순수 JPA에서 DTO 조회 MemberDto @Data public class MemberDto { private String username; private int age; public MemberDto(String username, int age) { this.username = username; this.age = age; } } 순수 JPA에서 DTO 조회 코드 @Test public void findDtoByJPQL() { List result = em.createQuery("select new study.querydsl.dto.MemberDto(m.username, m.age) from Member m", MemberDto.class) .getResultList(); for (MemberDt..
프로젝션과 결과 반환 - 기본
2022. 7. 25. 15:45
Querydsl
프로젝션: select 대상 지정 프로젝션 대상이 하나 @Test public void simpleProjection() { List result = queryFactory .select(member.username) .from(member) .fetch(); for (String s : result) { System.out.println("s = " + s); } } 프로젝션 대상이 하나면 타입을 명확하게 지정할 수 있음 프로젝션 대상이 둘 이상이면 튜플이나 DTO로 조회 튜플 조회 프로젝션 대상이 둘 이상일 때 사용 com.querydsl.core.Tuple @Test public void tupleProjection() { List result = queryFactory .select(member...
상수, 문자 더하기
2022. 7. 25. 15:35
Querydsl
상수가 필요하면 Expressions.constant(xxx) 사용 @Test public void constant() { List result = queryFactory .select(member.username, Expressions.constant("A")) .from(member) .fetch(); for (Tuple tuple : result) { System.out.println("tuple = " + tuple); } } 결과 : member1, A 참고: 위와 같이 최적화가 가능하면 SQL에 constant 값을 넘기지 않는다. 상수를 더하는 것 처럼 최적화가 어려우면 SQL에 constant 값을 넘긴다. 문자 더하기 concat @Test public void concat() { Lis..
Case 문
2022. 7. 25. 15:24
Querydsl
select, 조건절(where), order by에서 사용 가능 단순한 조건 @Test public void basicCase() { List result = queryFactory .select(member.age .when(10).then("열살") .when(20).then("스무살") .otherwise("기타")) .from(member) .fetch(); for (String s : result) { System.out.println("s = " + s); } } 복잡한 조건 @Test public void complexCase() { List result = queryFactory .select(new CaseBuilder() .when(member.age.between(0, 20)).th..
서브 쿼리
2022. 7. 25. 13:31
Querydsl
com.querydsl.jpa.JPAExpressions 사용 서브 쿼리 eq 사용 /** * 나이가 가장 많은 회원 조회 */@Test public void subQuery() throws Exception { QMember memberSub = new QMember("memberSub"); List result = queryFactory .selectFrom(member) .where(member.age.eq( JPAExpressions .select(memberSub.age.max()) .from(memberSub) )) .fetch(); assertThat(result).extracting("age") .containsExactly(40); } 서브 쿼리 goe 사용 @Test public voi..
조인 - 페치 조인
2022. 7. 25. 11:12
Querydsl
페치 조인은 SQL에서 제공하는 기능은 아니다. SQL조인을 활용해서 연관된 엔티티를 SQL 한번에 조회하는 기능이다. 주로 성능 최적화에 사용하는 방법이다. 페치 조인 미적용 지연로딩으로 Member, Team SQL 쿼리 각각 실행 @PersistenceUnit EntityManagerFactory emf; @Test public void fetchJoinNo() { em.flush(); em.clear(); Member findMember = queryFactory .selectFrom(member) .where(member.username.eq("member1")) .fetchOne(); boolean loaded = emf.getPersistenceUnitUtil().isLoaded(findMe..
조인 - on절
2022. 7. 25. 10:09
Querydsl
ON절을 활용한 조인(JPA 2.1부터 지원) 1. 조인 대상 필터링 2. 연관관계 없는 엔티티 외부 조인 1. 조인 대상 필터링 예) 회원과 팀을 조인하면서, 팀 이름이 teamA인 팀만 조인, 회원은 모두 조회 /** * 예) 회원과 팀을 조인하면서, 팀 이름이 teamA인 팀만 조인, 회원은 모두 조회 * JPQL: SELECT m, t FROM Member m LEFT JOIN m.team t on t.name = 'teamA' * SQL: SELECT m.*, t.* FROM Member m LEFT JOIN Team t ON m.TEAM_ID=t.id and t.name='teamA' */ @Test public void join_on_filtering() throws Exception { L..
조인 - 기본 조인
2022. 7. 24. 21:21
Querydsl
기본 조인 조인의 기본 문법은 첫 번째 파라미터에 조인 대상을 지정하고, 두 번째 파라미터에 별칭(alias)으로 사용할 Q 타입을 지정하면 된다. join(조인 대상, 별칭으로 사용할 Q타입) @Test public void join() { List result = queryFactory .selectFrom(member) .join(member.team, team) .where(team.name.eq("teamA")) .fetch(); assertThat(result).extracting("username").containsExactly("member1", "member2"); } join() , innerJoin() : 내부 조인(inner join) leftJoin() : left 외부 조인(le..
집합
2022. 7. 24. 20:49
Querydsl
집합 함수 /** * JPQL * select * COUNT(m), //회원수 * SUM(m.age), //나이 합 * AVG(m.age), //평균 나이 * MAX(m.age), //최대 나이 * MIN(m.age) //최소 나이 * from Member m */ @Test public void aggregation() throws Exception { List result = queryFactory .select(member.count(), member.age.sum(), member.age.avg(), member.age.max(), member.age.min()) .from(member) .fetch(); Tuple tuple = result.get(0); assertThat(tuple.get(m..