이 말은 JPQL 에서 쿼리에서 오타가 발생해도 컴파일 시점에서 알기 힘들다. 오로지 런타임에서만 체크가 가능하다. 하지만 Querydsl 은 컴파일 시점에 오류를 잡아줄 수 있기 떄문에 좋다.
Querydsl 동작 하는 과정은 JPQL 을 거쳐서 SQL 로 변환되서 실행한다.
.join(member.team, team1)
에 넣어주는 team1은 QTeam을 선언해서 넣어줘야한다ON 절과 WHERE 절의 차이는 ON 절 같은 경우는 JOIN 할 데이터를 필터하기 위해서 사용하는 반면에 WHERE 절은 JOIN 을 하고나서 데이터를 필터하기 위해서 사용한다고 생각하면 된다. 즉 ON 절이 WHERE 절보다 먼저 실행이 되고 이는 LEFT_OUTER_JOIN 을 하면 뚜렷히 드러난다.
@Test
@Transactional
@DisplayName("예) 회원과 팀을 조인하면서, 팀 이름이 TeamA인 팀만 조인 하고 회원은 모두 조회한다.")
void joinOnFiltering(){
//given
biSetting();
QMember member = QMember.member;
QTeam team = QTeam.team;
//when
List<Tuple> result = factory
.select(member, team)
.from(member)
.leftJoin(member.team, team)
.on(team.name.eq("team1"))
.fetch();
//then
for (Tuple tuple : result) {
System.out.print(tuple.get(member).getName());
if(tuple.get(team)!=null) System.out.println(" "+tuple.get(team).getName());
else System.out.println(" null");
}
}
left outer join을할때는 on과 where사용에 유의하자
emf.getPersistenceUnitUtil().isLoaded(m2));
조건에 따라서 값을 지정해주는 CASE 문은 select, 조건절(where), orderBy 에서 사용이 가능하다.
상수가 필요하다면 Expressions.constant() 를 사용하면 된다. 줄여서 쓰고 싶다면 static import 를 사용하자.
concat사용
Projection 은 엔터티를 그냥 그대로 가지고 오는게 아니라 필요한 필드만 가지고 오는 걸 말한다.
Querydsl 에서는 프로젝션 대상이 하나면 명확한 타입을 지정할 수 있지만 프로젝션 대상이 둘 이상이라면 Tuple 이나 DTO 로 조회해야 한다.
@Test
void projectionWithJpa(){
//given
//when
List<MemberDto> result = em.createQuery(
"select new com.study.querydsl.dto.MemberDto(m.username, m.age)" +
"from Member m", MemberDto.class
)
.getResultList();
//then
for (MemberDto memberDto : result){
System.out.println(memberDto.toString());
}
}
@Test
void 서브쿼리_projection(){
//given
QMember member1 = new QMember("M");
//when
List<jpaStudy.ex.dto.MemberDto> result = factory.select(Projections.fields(jpaStudy.ex.dto.MemberDto.class, QMember.member.name,
ExpressionUtils.as(
JPAExpressions.select(member1.age.max()).from(member1), "age"
)
)).from(QMember.member).fetch();
//then
result.forEach(e -> {
System.out.println(e.getName() + " " + e.getAge());
Assertions.assertThat(e.getAge()).isEqualTo(113);
});
}
new QMemberDto(member.username, member.age)
와 같이 사용하면 된다select 는 다음과 같은 과정을 거친다
1. flush
2. 디비로 쿼리날림
3. 쿼리결과가 영속성컨텍스트에 있으면 버리고 없으면 저장 .
update는 업데이트된 객체를 가져오진 않는다.
그래서 1,2 과정만 거친다
쿼리dsl에서는 insert가 없어서 persist를 사용한다
JPAQueryFactory는 insert()를 가지고 있지 않다.
insert를 하려면 EntityManager를 사용하자! (SQLQueryFactory는 가능하다)
public List<MemberTeamDto> searchByWhere(MemberSearchCondition condition){
return queryFactory.select(new QMemberTeamDto(
QMember.member.id.as("memberId"),
QMember.member.name.as("username"),
QMember.member.age,//생성자 순서에 잘 맞춰야함
QTeam.team.id.as("teamId"),
QTeam.team.name.as("teamName")
)).from(QMember.member).leftJoin(QMember.member.team, QTeam.team).where(teamNameEq(condition.getTeamName()),memberNameEq(condition.getUsername()), ageGoe(condition.getAgeGoe()), ageLoe(condition.getAgeLoe())).fetch();
}
java.lang.NullPointerException: Cannot invoke "com.querydsl.core.types.Expression.accept(com.querydsl.core.types.Visitor, Object)" because "expr" is null
public List<Member> test(MemberSearchCondition condition){
return queryFactory.select(QMember.member)
.from(QMember.member, QTeam.team)
.where(memberNameEq(condition.getUsername()))
.fetch();
}
@Test
@Transactional
//TODO: 왜이거안붙이면 프록시초기화안됐다더? 그이유는 트랜잭션안에있어야 영속성컨텍스트의 관리를바당서그런건가"?
//repository에 붙은 트랜잭션ㅇ이 끝나면서 프록시 객체인 team의 name을 읽으려해서 그런거임
void 왜네번이나출력되지(){
//given
//when
List<Member> list = repository.test(new MemberSearchCondition("mem1", null , null,null));
//then
list.forEach(e -> {
System.out.println(e.getName() + " " + e.getTeam().getName() + " " + e.getId() + " " );
});
}
public List<Member> test(MemberSearchCondition condition){
return queryFactory.selectFrom(QMember.member).join(QMember.member.team, QTeam.team).fetchJoin()
.where(memberNameEq(condition.getUsername()))
.fetch();
}
출처
https://github.com/Youngerjesus/Querydsl/blob/master/docs/basic.md
https://jojoldu.tistory.com/533