Life Developer
인생 개발자
Developer (136)
[JPA]JPQL - distinct 중복제거

String query = "select t from Team t join fetch t.members";
List<Team> resultList = em.createQuery(query, Team.class).getResultList();

System.out.println("resultList = " + resultList.size());
for (Team t : resultList) {
System.out.println("t.getName()+\"|\"+t.getMembers().size()+\"\" = " + t.getName()+"|"+t.getMembers().size()+"");
for (Member m : t.getMembers()) {
System.out.println("m = " + m);
}
}

 

이걸 실행하면 

 

이렇게 나온다.

 

중복값이 생긴다는 것이다.

 

물론 중복값을 일부러 얻을 때도 분명 있을것이다.

 

그럴 경우가 아닐때 제외를 시키고 싶다면 어케할까?

 

쿼리에 distinct를 써주면 된다.

 

String query = "select distinct t from Team t join fetch t.members";

 

그래서 이렇게 바꾸고 db에서 돌아갈 쿼리를 생각해보면 이상하다.

 

db에서는 아래 표에서 모든 컬럼의 값이 일치해야 중복이 제거된다.

 

 

그런데 distinct 를 넣었을 뿐인데 

 

이렇게 나온다...

 

중복제거가 되긴했다.

 

이 의미는 db에서 중복제거를 하지않았고, JPA가 직접 중복제거를 했다는 소리다.

 

SQL의 distinct 만으로는 중복제거를 100% 하지 못한다는 말이다.

 

그렇다! JPA에서 distinct 쿼리가 포함된 JPQL을 보면 같은 식별자를 바라보고 있는 Entity를 대상으로 중복제거를

 

해준다.

 

증말 똑똑한 놈이다.

'Developer' 카테고리의 다른 글

[JPA]BatchSize - LAZY로딩 시 몇개를 가져올거냐?  (0) 2020.09.22
[JAP]fetch 조인 - 특징과 한계  (0) 2020.09.22
[JAP]fetch 조인 - 한방쿼리  (0) 2020.09.22
[JPA]JPQL 기본 함수  (0) 2020.09.21
[JPA]COALESCE와 NULLIF  (0) 2020.09.21
  Comments,     Trackbacks
[JAP]fetch 조인 - 한방쿼리

fetch가 무엇인가?

 

join 할때 한방에 다 가져오는 전략. fetch 전략!

 

Entity만들때 FK를 클래스에서 지정하고 fetch=FetchType~~~어쩌고저쩌고

 

이렇게 쓴적이있다. ex ) @ManyToOne(fetch=FetchType.LAZY)

 

이 fetch는 LAZY, 즉, 지연로딩에 관한 것이었고

 

지금 사용하는 이 fetch는 쿼리문 작성할때 사용한다.

 

아래처럼 join 쿼리 작성시 끼워넣는다.

 

String query = "select m from Member m join fetch m.team";

 

일단 fetch를 사용하지 않고 아래를 실행해보자.

 

Team team = new Team();
team.setName("teamA");
em.persist(team);

Team team1 = new Team();
team1.setName("teamB");
em.persist(team1);

Member member = new Member();
member.setUsername("userA");
member.setAge(10);
member.setType(MemberType.ADMIN);

Member member1 = new Member();
member1.setUsername("userB");
member1.setAge(70);
member1.setType(MemberType.USER);

Member member2 = new Member();
member2.setUsername("userC");
member2.setAge(90);
member2.setType(MemberType.USER);

member.setTeam(team);
member1.setTeam(team1);
member2.setTeam(team1);

em.persist(member);
em.persist(member1);
em.persist(member2);


em.flush();
em.clear();

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

for (Member member : resultList) {
System.out.println("member = " + member.getUsername()+","+member.getTeam().getName());
}

System.out.println(" ======================================================= ");
tx.commit(); //이시점에 DB 쿼리가 날라감

 

 

 

실행을 하면 em.clear(); 이후에 쿼리는 select m from Member m 이때 member만 일단 select 쿼리가 나가게 된다.

 

왜냐하면 Member 에서 Team변수에 지연로딩을 걸어놨기 때문에 조인이 안된 상태로 Member만 select 때린다.

 

이때 proxy 객체를 얻어온다. (그냥 객체가 아니다, 왜냐면 지연로딩이기 때문에 - 지연로딩= 프록시객체 얻어옴)

 

그리고 아래에 member3.getTeam().getName() 을 딱 호출하면 team과 연관된 데이터를 건드렸기 때문에 team select

 

문이 호출되면서 프록시 객체가 없어지고 그냥 일반 객체로 변하게 된다.

 

그러면 teamA에 관한 첫번째 member가 출력이 된다. 만약 teamA가 2명이면 2명의 member가 출력되겠지?

 

그러고 또다시 루프를 돌면서 두번째 member에 대해 member.getTeam().getName() 를 할것이고 이때 Team은

 

teamA가 아니라 teamB라서 또다시 DB에서 쿼리를 가져온다.... 뭔가 복잡하다.

 

이때 teamB는 2명이다. 두번째 member일때 select 쿼리를 때리고 세번째 member는 또다시 teamB이기 때문에

 

영속성 컨텍스트에 이미 담긴 놈을 불러오므로 DB에 쿼리를 안때리는것이다..

 

개어렵노.ㅡㅡ

 

지금 적는것 조차 뭔가 복잡하고 알아듣기 어렵지만... 내 자신은 이해를 완벽히 해서 다행일뿐이다.

 

 

========================================================================

 

결론은 팀이 다른 100명의 멤버를 만약 루프를 돌리면 team select 쿼리를 100번 때리게 된다는 것이다.

 

겁나 비효율적이지 않나?

 

그래서 사용하는게 fetch이다.

 

사용법은 간단하다. 

 

String query = "select m from Member m join fetch m.team";

 

이렇게 하면된다.

 

이렇게 사용하면 join 할때 아예 전체 데이터를 DB에서 가져온다.

 

그래서 실행하면.

 

이렇게 getResultList 할때 딱 한번만 join해서 가져오고 그뒤에 루프돌때는 DB에 안간다.

 

그래서 루프 돌때 team객체는 프록시 객체가 아니다. 그냥 실제 엔티티다.

 

 

이게바로 fetch 조인이다....실무에서 겁나 쓴다. 기억하자

'Developer' 카테고리의 다른 글

[JAP]fetch 조인 - 특징과 한계  (0) 2020.09.22
[JPA]JPQL - distinct 중복제거  (0) 2020.09.22
[JPA]JPQL 기본 함수  (0) 2020.09.21
[JPA]COALESCE와 NULLIF  (0) 2020.09.21
[JPA]조건식 - CASE  (0) 2020.09.21
  Comments,     Trackbacks
[JPA]JPQL 기본 함수

 

 

'Developer' 카테고리의 다른 글

[JPA]JPQL - distinct 중복제거  (0) 2020.09.22
[JAP]fetch 조인 - 한방쿼리  (0) 2020.09.22
[JPA]COALESCE와 NULLIF  (0) 2020.09.21
[JPA]조건식 - CASE  (0) 2020.09.21
[JPA]서브쿼리  (0) 2020.09.21
  Comments,     Trackbacks
[JPA]COALESCE와 NULLIF

COALESCE

하나씩 조회해서 NULL이 아니면 반환한다. NULL이면 명시한것을 반환

 

 

String query = "select coalesce(m.username,'이름없는 회원') " +
"from Member m ";
List<String> resultList = em.createQuery(query, String.class).getResultList();

 

 

NULLIF

두값이 같으면 null반환, 다르면 첫번째 값 반환

 

 

String query = "select nullif(m.username,'admin') " +
"from Member m ";
List<String> resultList = em.createQuery(query, String.class).getResultList();

'Developer' 카테고리의 다른 글

[JAP]fetch 조인 - 한방쿼리  (0) 2020.09.22
[JPA]JPQL 기본 함수  (0) 2020.09.21
[JPA]조건식 - CASE  (0) 2020.09.21
[JPA]서브쿼리  (0) 2020.09.21
[JPA]JOIN  (0) 2020.09.21
  Comments,     Trackbacks
[JPA]조건식 - CASE

Team team = new Team();
team.setName("teamA");
em.persist(team);

Member member = new Member();
member.setUsername("userA");
member.setAge(10);
member.setType(MemberType.ADMIN);

Member member1 = new Member();
member1.setUsername("userB");
member1.setAge(70);
member1.setType(MemberType.USER);

member.setTeam(team);
member1.setTeam(team);

em.persist(member);
em.persist(member1);


em.flush();
em.clear();

String query ="select case when m.age<=10 then '학생요금'" +
"when m.age>=60 then '경로요금'" +
"else '일반요금' end" +
" from Member m";
List<String> resultList = em.createQuery(query, String.class).getResultList();

for (String s : resultList) {
System.out.println("s = " + s);
}

System.out.println(" ======================================================= ");
tx.commit(); //이시점에 DB 쿼리가 날라감

'Developer' 카테고리의 다른 글

[JPA]JPQL 기본 함수  (0) 2020.09.21
[JPA]COALESCE와 NULLIF  (0) 2020.09.21
[JPA]서브쿼리  (0) 2020.09.21
[JPA]JOIN  (0) 2020.09.21
[JPA]페이징  (0) 2020.09.21
  Comments,     Trackbacks
[JPA]서브쿼리

JPA에서는 인라인뷰가 안먹힌다.

스칼라 서브쿼리랑 서브쿼리는 다 사용가능.

 

스칼라 서브쿼리

String query = "select (select m1 from Member m1) from Member m left join Team t on m.username=t.name";

 

뭐이런거나

 

 

 

서브쿼리
String query = "select m from Member m where m.age<(select m1.age from Member m1 where m1.id=1)";

 

뭐 이런거 사용가능하단 얘기

'Developer' 카테고리의 다른 글

[JPA]COALESCE와 NULLIF  (0) 2020.09.21
[JPA]조건식 - CASE  (0) 2020.09.21
[JPA]JOIN  (0) 2020.09.21
[JPA]페이징  (0) 2020.09.21
[JPA]프로젝션 - 어떻게 select를 할지  (0) 2020.09.21
  Comments,     Trackbacks
[JPA]JOIN

INNER JOIN (inner 생략가능)

 

Team team = new Team();
team.setName("teamA");
em.persist(team);

Member member = new Member();
member.setUsername("member1");
member.setAge(10);

member.setTeam(team);

em.persist(member);


em.flush();
em.clear();

String query = "select m from Member m join m.team t where t.name=:teamName";
List<Member> result = em.createQuery(query, Member.class)
.setParameter("teamName","teamA").getResultList();


System.out.println(" ======================================================= ");
tx.commit(); //이시점에 DB 쿼리가 날라감

 

 

 

 

LEFT OUTER JOIN(또는 RIGHT) (outer 생략가능 - 오라클과 같음)

 

String query = "select m from Member m left join m.team t where t.name=:teamName";
List<Member> result = em.createQuery(query, Member.class)
.setParameter("teamName","teamA").getResultList();

 

 

 

세타조인(크로스조인, 카티션 프로덕트, 막조인)

 

String query = "select m from Member m, Team t where m.username=t.name";
List<Member> result = em.createQuery(query, Member.class)
.getResultList();

 

System.out.println("result.size() = " + result.size());

'Developer' 카테고리의 다른 글

[JPA]조건식 - CASE  (0) 2020.09.21
[JPA]서브쿼리  (0) 2020.09.21
[JPA]페이징  (0) 2020.09.21
[JPA]프로젝션 - 어떻게 select를 할지  (0) 2020.09.21
[JPA]Native Query  (0) 2020.09.21
  Comments,     Trackbacks
[JPA]페이징

JPA에서는 페이징 관련 API를 지원한다.

 

진짜 개꿀이다.

 

List<Member> result = em.createQuery("select m from Member m order by m.age desc", Member.class)
.setFirstResult(45).setMaxResults(10).getResultList();

 

그냥 위처럼 사용하면 된다. 타입을 아니까 타입 적어서 List로 빼면 된다.

뺄때 setFirstResult()와 setMaxResults() 를 써주면 개꿀빤다.

 

rownum에 인라인뷰 3개에 뭐 이딴 짓 안해도됨.ㅡㅡ

 

desc를 써주는것은 최근 게시물일 경우 니까 쓰는것이것지.

 

 

for (int i=0; i<100; i++){
Member member = new Member();
member.setUsername("member"+i);
member.setAge(i);
em.persist(member);
}

em.flush();
em.clear();

List<Member> result = em.createQuery("select m from Member m order by m.age desc", Member.class)
.setFirstResult(45).setMaxResults(10).getResultList();

System.out.println("result.size() = " + result.size());
for (Member member1 : result) {
System.out.println("member1 = " + member1);
}

'Developer' 카테고리의 다른 글

[JPA]서브쿼리  (0) 2020.09.21
[JPA]JOIN  (0) 2020.09.21
[JPA]프로젝션 - 어떻게 select를 할지  (0) 2020.09.21
[JPA]Native Query  (0) 2020.09.21
[JPA]QueryDSL - 동적쿼리 실무사용권장  (0) 2020.09.21
  Comments,     Trackbacks