Member
@Entity
@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@ToString(of={"id","username","age"})
public class Member {
@Id
@GeneratedValue
private Long id;
private String username;
private int age;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "team_id")
private Team team;
public Member(String username) {
this(username, 0);
}
public Member(String username, int age) {
this(username, age, null);
}
public Member(String username, int age, Team team) {
this.username = username;
this.age = age;
if (team != null) {
changeTeam(team);
}
}
public void changeTeam(Team team) {
this.team = team;
team.getMembers().add(this);
}
}
ToString은 가급적이면 연관관계에 해당하지 않는 걸로 설정NoArgsConstructor => protected로 외부에서 접근 못하게 막음changeTeam으로 양방향 연관관계 한번에 처리하기Team
@Entity
@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@ToString(of = {"id", "name"})
public class Team {
@Id
@GeneratedValue
@Column(name = "team_id")
private Long id;
private String name;
@OneToMany(mappedBy = "team")
private List<Member> members=new ArrayList<>();
public Team(String name) {
this.name = name;
}
}
Member이므로 Team에서는 조회만 가능함JPQL
@Test
void startJpql(){
String qlString= "select m from Member m " +
"where m.username = :username";
Member findMember = (Member)em.createQuery(qlString).setParameter("username","member1").getSingleResult();
assertThat(findMember.getUsername()).isEqualTo("member1");
}
QueryDSL
@Test
void startQuerydls(){
JPAQueryFactory queryFactory = new JPAQueryFactory(em);
QMember m= new QMember("m");
Member findMember = queryFactory.select(m)
.from(m)
.where(m.username.eq("member1"))
.fetchOne();
assertThat(findMember.getUsername()).isEqualTo("member1");
}
EntityManager로 JPAQueryFactory 생성@BeforeEach
public void before() {
queryFactory = new JPAQueryFactory(em);
//...
}
EntityManager가 크랜잭션 마다 별도의 영속성 컨텍스트를 제공하므로 동시성 문제는 걱정하지 않아도 됨기본 Q-Type 활용
QMember qMember= new QMember("m");
QMember qMember = QMember.member
static import와 함께 사용 가능@Test
void search(){
Member findMember= queryFactory.select(member).from(member).where(member.username.eq("member1").and(member.age.eq(10))).fetchOne();
assertThat(findMember.getUsername()).isEqualTo("member1");
}
and,or를 메서드 체인으로 연결이 가능select,from을 select from으로 합치기도 가능eq(member1): =ne(member1): !=eq(member1).not(): !=isNotNull(): is not nullin(a,b)notIn(a,b)between(a,b)goe(10) : >=gt(10): >loe(10): <=lt(10): <like(member1%)contains(member) : like %member1%startsWith(member): like member% @Test
void searchAndParam(){
Member findMember = queryFactory.selectFrom(member).where(member.username.eq("member1"),member.age.eq(10)).fetchOne();
assertThat(findMember.getUsername()).isEqualTo("member1");
}
where()에 파라미터로 검색조건을 추가하면 And 조건이 추가됨null값은 무시 => 메서드 추출을 활용해서 깔끔한 동적 쿼리를 만들 수 있다fetch(): 리스트 조회, 데이터 없으면 빈 리스트 반환fetchOne(): 단건 조회nullfetchFirst(): limit(1).fetchOne()fetchResults(): 페이징 정보 포함, total count 쿼리 추가 실행fetchCount(): count 쿼리로 변경해서 count 수 조회@Test
void sort(){
em.persist(new Member(null,100));
em.persist(new Member("member5",100));
em.persist(new Member("member6",100));
List<Member> result = queryFactory.selectFrom(member).where(member.age.eq(100)).orderBy(member.age.desc(), member.username.asc().nullsLast()).fetch();
System.out.println(result.toString());
Member member5 = result.get(0);
Member member6 = result.get(1);
Member member7 = result.get(2);
assertThat(member5.getUsername()).isEqualTo("member5");
assertThat(member6.getUsername()).isEqualTo("member6");
assertThat(member7.getUsername()).isNull();
}
desc(),asc(): 일반 정렬nullsLast(),nullsFirst(): null 데이터 순서 부여조회 건수 제한
@Test
void paging() {
List<Member> result = queryFactory.selectFrom(member)
.orderBy(member.username.desc())
.offset(1)
.limit(2)
.fetch();
}
offset(1): 0부터 조회limit(2): 최대 2건 조회전체 조회 수
@Test
void paging2(){
QueryResults<Member> result = queryFactory.selectFrom(member)
.orderBy(member.username.desc())
.offset(1)
.limit(2)
.fetchResults();
assertThat(result.getTotal()).isEqualTo(4);
assertThat(result.getLimit()).isEqualTo(2);
assertThat(result.getOffset()).isEqualTo(1);
}
집합 함수
COUNT(m) : 회원 수SUM(m.age): 나이 합AVG(m.age): 평균 나이MAX(m.age): 최대 나이MIN(m.age): 최소 나이사용 예시
@Test
void aggregation(){
List<Tuple> fetch = queryFactory.select(member.count(), member.age.sum(), member.age.avg(), member.age.max(), member.age.min()).from(member).fetch();
Tuple tuple = fetch.get(0);
assertThat(tuple.get(member.count())).isEqualTo(4) ;
assertThat(tuple.get(member.age.sum())).isEqualTo(100);
}
@Test
void group() throws Exception{
List<Tuple> result = queryFactory.select(team.name,member.age.avg())
.from(member).join(member.team,team).groupBy(team.name).fetch();
Tuple teamA = result.get(0);
Tuple teamB = result.get(1);
assertThat(teamA.get(team.name)).isEqualTo("teamA");
assertThat(teamA.get(member.age.avg())).isEqualTo(15);
assertThat(teamB.get(team.name)).isEqualTo("teamB");
assertThat(teamB.get(member.age.avg())).isEqualTo(35);
}
Having 사용join(조인대상, 별칭으로 사용할 Q 타입)
기본조인
@Test
void join(){
List<Member>result= queryFactory.select(member).from(member)
.groupBy(member.team,team).where(team.name.eq("teamA")).fetch();
assertThat(result).extracting("username").containsExactly("member1","member2");
}
join(),innerJoin(): 내부조인leftJoin: left 외부 조인rightJoin(); right 외부 조인세타조인
@Test
void setterJoin(){
em.persist(new Member("teamA"));
em.persist(new Member("teamB"));
List<Member> result = queryFactory.select(member).from(member, team).where(member.username.eq(team.name)).fetch();
assertThat(result).extracting("username").containsExactly("teamA","teamB");
}
조인 - ON 절
@Test
void join_on_filterring(){
List<Tuple> result = queryFactory.select(member,team).from(member)
.leftJoin(member.team,team).on(team.name.eq("teamA")).fetch();
for(Tuple tuple : result){
System.out.println("tuple : "+tuple);
}
}
teamA인 팀만 조인연관관계 없는 조인
@Test
void join_on_no_realation(){
em.persist(new Member("teamA"));
em.persist(new Member("teamB"));
List<Tuple> result = queryFactory.select(member,team).from(member)
.leftJoin(member.team,team).on(member.username.eq("teamA")).fetch();
for(Tuple tuple : result){
System.out.println("tuple : "+tuple);
}
}
leftJoin() 부분에 일반 조인과 다르게 엔터티 하나만 들어감조인 - 페치조인
@Test
void fetchJoin(){
em.flush();
em.clear();
Member findMember = queryFactory.selectFrom(member).join(member.team,team).fetchJoin().where(member.username.eq("member1"))
.fetchOne();
boolean loaded = emf.getPersistenceUnitUtil().isLoaded(findMember.getTeam());
}
Team까지 조회하지 않음Team까지 한꺼번에 가져옴예시 1
@Test
void subQuery(){
QMember qmember= new QMember("memberSub");
List<Member> result = queryFactory.select(member).from(member)
.where(member.age.eq(JPAExpressions.select(qmember.age.max())
.from(qmember))).fetch();
assertThat(result).extracting("age").containsExactly(40);
}
JPAExpressions 사용예시 2
@Test
void subQueryGoe(){
QMember memberSub =new QMember("memberSub");
List<Member> result = queryFactory.selectFrom(member).where(member.age.goe(
JPAExpressions.select(memberSub.age.avg())
.from(memberSub)
)).fetch();
assertThat(result).extracting("age").containsExactly(30,40);
}
In 서브 쿼리
@Test
void subQueryIn(){
QMember memberSub =new QMember("memberSub");
List<Member> result = queryFactory.selectFrom(member)
.where(member.age.in(
JPAExpressions.select(memberSub.age).from(memberSub)
.where(memberSub.age.gt(10))
)).fetch();
assertThat(result).extracting("age").containsExactly(20,30,40);
}
단순한 조건
@Test
void basicCase(){
List<String> result =queryFactory.select(member.age
.when(10).then("10살")
.when(20).then("20살")
.otherwise("기타"))
.from(member)
.fetch();
for(String str : result){
System.out.println("s =" +str);
}
}
복잡한 조건
@Test
void complexCase(){
List<String> result = queryFactory.select(new CaseBuilder()
.when(member.age.between(0, 20)).then("0~20살")
.when(member.age.between(21, 30)).then("21~30살")
.otherwise("기타"))
.from(member)
.fetch();
for(String str : result){
System.out.println("s =" +str);
}
}
orderBy에서 Case문 함께 사용하기
NumberExpressions<Integer> rankPath= new CaseBuilder();
.when(member.age.between(0,20)).then(2)
.when(member.age.between(21,30)).then(1)
.otherwise(3);
List<Tuple> result = queryFactory
.select(member.username, member.age, rankPath)
.from(member)
.orderBy(rankPath.desc())
.fetch();
for (Tuple tuple : result) {
String username = tuple.get(member.username);
Integer age = tuple.get(member.age);
Integer rank = tuple.get(rankPath);
System.out.println("username = " + username + " age = " + age + " rank = " +rank);
@Test
void constant(){
List<Tuple> result = queryFactory.select(member.username, Expressions.constant("A"))
.from(member).fetch();
for(Tuple tuple : result){
System.out.println("tuple : "+tuple);
}
}
@Test
void concat(){
List<String> result = queryFactory.select(member.username.concat("_").concat(member.age.stringValue())).from(member).where(member.username.eq("member1")).fetch();
for(String s: result){
System.out.println("s =" +s);
}
}
출처: https://www.inflearn.com/course/querydsl-%EC%8B%A4%EC%A0%84