JAVA

[Querydsl] 03. 기본 문법 (fetchJoin, case, 상수문자더하기)

코린이s 2024. 3. 31. 12:32
728x90

이번 시간에는 기본 문법 중 fetchjoin 사용 방법 , cese, 상수문자 더하는 방법을 알아보자!

우선 테스트를 위해 테스트 코드가 실행 되기전 초기 데이터 셋팅하는 코드를 작성 한다.

@BeforeEach
public void testEntity(){
    query = new JPAQueryFactory(em);

    Team teamA = Team.builder()
          .name("teamA")
          .build();
    Team teamB = Team.builder()
          .name("teamB")
          .build();
    em.persist(teamA);
    em.persist(teamB);
    Member member1 = Member.builder()
          .username("member1")
          .age(10)
          .team(teamA)
          .build();
    Member member2 = Member.builder()
          .username("member2")
          .age(10)
          .team(teamA)
          .build();
    Member member3 = Member.builder()
          .username("member3")
          .age(20)
          .team(teamB)
          .build();
    Member member4 = Member.builder()
          .username("member4")
          .age(20)
          .team(teamB)
          .build();

    em.persist(member1);
    em.persist(member2);
    em.persist(member3);
    em.persist(member4);

    // 초기화
    em.flush();
    em.clear();

    List<Member> members = em.createQuery("select m from Member m", Member.class).getResultList();

    //log.error("[data] => {}", members);

}

이제 Fetch Join 하는 방법을 알아보자!

EntityManagerFactory 를 사용하면 영속성 컨텍스트에 해당 데이터가 있는지 알 수 있다.

fetch join 을 사용하지 않을시 team 을 조회하지 않았으므로 영속 컨텍스트에 없을 것이다.

그럼 isLoaded 의 결과를 false 로 예상할 수 있으며 실행해보자!

 

@PersistenceUnit
	EntityManagerFactory emf;
    
@Test
	public void fetchJoin(){

		JPAQueryFactory queryFactory = new JPAQueryFactory(em);
		Member findMember = queryFactory
				.selectFrom(member)
				.where(member.username.eq("member1"))
				.fetchOne();
		boolean loaded = emf.getPersistenceUnitUtil().isLoaded(findMember.getTeam());
		// 패치 조인 미적용
        Assertions.assertFalse(loaded);
	}

정상적으로 false 가 나왔다.

아래의 경우에도 fetch조인을 따로 사용하지 않았으므로 동일하게 false 가 나와야 한다.

@Test
	public void fetchJoin(){

		JPAQueryFactory queryFactory = new JPAQueryFactory(em);
		Member findMember = queryFactory
				.selectFrom(member)
				.join(member.team, team)
				.where(member.username.eq("member1"))
				.fetchOne();
		boolean loaded = emf.getPersistenceUnitUtil().isLoaded(findMember.getTeam());
		// 패치 조인 미적용
        Assertions.assertTrue(loaded);
	}

정상적으로 true 가 나와서 테스트에 실패한것을 볼 수 있다.(위와 다르게 assertFalse가 아닌 assertTrue인점 참고)

이제 위와 코드는 동일하게 하고 join 뒤에 fetchJoin() 을 붙이면 fetchjoin 으로 동작하여 team 엔티티도 불러오게 된다.

	@Test
	public void fetchJoin(){

		JPAQueryFactory queryFactory = new JPAQueryFactory(em);
		Member findMember = queryFactory
				.selectFrom(member)
				.join(member.team, team).fetchJoin()
				.where(member.username.eq("member1"))
				.fetchOne();
		boolean loaded = emf.getPersistenceUnitUtil().isLoaded(findMember.getTeam());
		// 패치 조인 미적용
        Assertions.assertTrue(loaded);
	}

그래서 결과는 정상적으로 true 이다.

이제 서브 쿼리 사용 방법을 알아보자!

// where 절에 사용하는 방법

아래와 같이 JPAExpressions 를 사용하면 되며 그 뒤에 사용하는 방법은 동일하다.

@Test
public void subQuery(){
    QMember memberSub = new QMember("memberSub");
    List<Member> result = query.selectFrom(member)
          .where(member.age.eq(
                JPAExpressions
                      .select(memberSub.age.max())
                      .from(memberSub)
          ))
          .fetch();

    Assertions.assertTrue(result.stream().map(Member::getAge).filter(s -> s.equals(40)).toList().isEmpty());
}

위의 결과는 age max가 현재 20 이니 40인 데이터가 없어 결과가 없는게 맞으니 true 가 되어야 한다.

정상적으로 테스트 통과 하였다.

// select 에 서브 쿼리 사용 방법 

select 절에도 동일하게 사용가능하며 위치만 where 에서 select 로 변경 된것이므로 따로 설명은 하지 않을 예정이다.

@Test
public void selectSubQuery(){
    List<Tuple> fetch = query.select(
          member.age,
          JPAExpressions.select(member.age.max())
                .from(member)
    ).from(member).fetch();
    for(Tuple t : fetch){
       log.error("[DATA] {}", t);
    }
}

print 한 결과 정상적으로 max 데이터가 출력 되었다.

// 서브쿼리 from 절

querydsl 에서 from 절에 서브쿼리가 불가능하다...! (절망)

그래서 서브쿼리를 join 절로 수정, 쿼리를 두번 분리해서 사용, nativeSQL을 사용해야 한다.

이제 Case에 대해서 알아보자!

어렵지 않게 '.' 으로 이어서 바로 when, then, otherwise를 사용하면 되며

@Test
public void basicCase(){
    List<String> fetch = query.select(member.age.when(10).then("열살")
                .when(20).then("스무살")
                .otherwise("기타"))
          .from(member)
          .fetch();
    for (String s : fetch){
       log.error("[data] {}", s);
    }

}

출력도 정상적으로 되었다.

만약에 복잡한 케이스문을 사용하고자 하면 (case 조건에 들어가는 대상이 한개가 아닐때 등) CaseBuilder 를 사용하면 된다!

@Test
public void complexCase(){
    List<String> fetch = query.select(new CaseBuilder()
                .when(member.age.between(0, 20)).then("0~20")
                .otherwise("기타'"))
          .from(member)
          .fetch();
    for (String s : fetch){
       log.error("[data] {}", s);
    }
}

결과도 정상적으로 출력된다.

이제 상수 문자 더하기를 알아보자!

A 라는 문자를 추가로 출력 하는 예시는 아래와 같으며 Expressions 의 constant 를 사용하면 'A' 문자를 출력한다.

@Test
public void constant(){
    List<Tuple> result = query
          .select(member.username, Expressions.constant("A"))
          .from(member)
          .fetch();
    for (Tuple t : result){
       log.error("[data] {}", t);
    }
}

쿼리 날라가는것을 확인하면 JPQL, 실제쿼리 모두 'A' 를 쿼리로 호출 하지 않고 붙여주는것을 알 수 있다.

결과도 정상적으로 나왔다.

이제 concat 을 통해 문자를 붙여보도록 하자!

바로 '.concat()'으로 붙일 수 있으며 string 이 아니면 'stringValue()'를 통해 string으로 변경하여 붙이면 된다.

// {username}_{age}
@Test
public void concat(){
    List<String> result = query.select(member.username.concat("_").concat(member.age.stringValue()))
          .from(member)
          .fetch();
    for (String r : result){
       log.error("[data] {}", r);
    }
}

쿼리로 concat 함수가 호출 되어 처리 된다.

결과도 정상적인것을 볼 수 있다.

끝!

728x90