Life Developer
인생 개발자
Developer (136)
[JPA실전]시작

start.spring.io 에서 시작

'Developer' 카테고리의 다른 글

[JPA실전]devtools dependency  (0) 2020.09.28
[JPA실전]lombok 다운  (0) 2020.09.28
[JPA]벌크연산  (0) 2020.09.23
[JPA]JPQL-Named쿼리  (0) 2020.09.22
[JPA]쿼리에 Entity 직접 사용하기  (0) 2020.09.22
  Comments,     Trackbacks
[JPA]벌크연산

벌크연산 - 한방쿼리 - 영속성 컨텍스트 개무시하고 바로 DB에 쿼리 때림 

 

 

 

니 운동 좀 했는갑네.

몸이 벌크업됐노.

 

에서 벌크란 통? 이라고 할수가 있다. 통, 즉, 사이즈가 업됐노.

 

이렇게 생각할수 있는데.

 

벌크연산도 통연산 이라고 해석해도 될거같다.

 

통으로 연산 때리는 거다.

 

쿼리 한번으로 여러 테이블 로우 조져버리는거임.

 

update와 delete를 지원한다.

 

뭐 JPA 표준 스펙에는 없는데 하이버네이트가 지원하는것도 있다.

 

inset into ...select ~~ 이런거.

 

select해서 insert 조지는거...뭐 이런정도?

 

 

int resultCount = em.createQuery("update Member m set m.age=100").executeUpdate();
System.out.println("resultCount = " + resultCount);

 

em.createQuery에 update문 쌔리고 .executeUpdate 때리면 된다.

 

결과값은 변경된 컬럼수가 나옴.

 

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

 

벌크 연산은 영속성 컨텍스트를 무시해버리고 DB에 직접 쿼리 때린다.

잘못하면 망한다. 어떻게 하느냐? 

 

-벌크 연산 먼저 실행 (flush 자동으로 됨)

-벌크 연산 수행 후 영속성 컨텍스트를 초기화

 

하면 된다.

 

 

아래경우 나이가 모두 100으로 바뀌지 않고 출력이 된다.

 

왜냐면 자동 flush insert 다 때리고 clear를 안해줬으니 영속성 컨텍스트에는 값들이 남아있기 때문에.

 

 

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);

//flush 자동 호출
int resultCount = em.createQuery("update Member m set m.age=100").executeUpdate();
System.out.println("resultCount = " + resultCount);

System.out.println("member1.getAge() = " + member.getAge());
System.out.println("member1.getAge() = " + member1.getAge());
System.out.println("member1.getAge() = " + member2.getAge());

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

 

 

 

그럼 이때는?

 

~

~

~

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


//flush 자동 호출
int resultCount = em.createQuery("update Member m set m.age=100").executeUpdate();
System.out.println("resultCount = " + resultCount);

Member mem = em.find(Member.class, member.getId());

System.out.println("mem = " + mem.getAge());

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

 

 

 

ㅋㅋ 이때도 mem.getAge 해봤자 100이 안나옴..

 

왜냐하면 벌크연산은 DB로 바로 쿼리를 쳐때려버리고 영속성 컨텍스트를 개무시하니까.

 

즉, 영속성 컨텍스트에 남아있는 em.persist(member); 이 놈을 불러옴.ㅋ....미띤

 

 

그래서 clear를 여기 써주면 새로가져온다

 

int resultCount = em.createQuery("update Member m set m.age=100").executeUpdate();
System.out.println("resultCount = " + resultCount);

em.clear();

Member mem = em.find(Member.class, member.getId());

'Developer' 카테고리의 다른 글

[JPA실전]lombok 다운  (0) 2020.09.28
[JPA실전]시작  (0) 2020.09.28
[JPA]JPQL-Named쿼리  (0) 2020.09.22
[JPA]쿼리에 Entity 직접 사용하기  (0) 2020.09.22
[JPA]다형성 JPQL 쿼리  (0) 2020.09.22
  Comments,     Trackbacks
[JPA]JPQL-Named쿼리

어마어마한 메리트가 있다.

 

Named 쿼리란 미리 정의해서 이름을 부여해두고 사용하는 JPQL이다.

 

정적쿼리..변하지않음..(메리트있음)

 

어노테이션이나 xml에 정의 가능하다.

 

진짜장점 : 애플리케이션 로딩시점에 초기  화 후 재사용, 애플리케이션 로딩시점에 쿼리 검증 가능!!

 

도랐맨. 무슨말.

 

직접해보자.

 

 

Entity
@NamedQuery(name = "Member.findByUsername", query = "select m from Member m where m.username = :username")
public class Member {
@Id
@GeneratedValue
private Long id;
private String username;
private int age;

~

~

~

 

 

이렇게 선언해줌

 

사용은 이렇게.

 

List<Member> resultList = em.createNamedQuery("Member.findByUsername", Member.class).setParameter("username", "userC")
.getResultList();
for (Member mem : resultList) {
System.out.println("mem = " + mem);
}

 

 

그럼 기대값대로 나온다.

 

 

신기한것은 애플 실행할때

 

Named 쿼리에 오타가 있으면 에러발생시킴.

 

@NamedQuery(name = "Member.findByUsername", query = "selectt m from Member m where m.username = :username")

 

select를 selectt로 바꾸고 하니 에러가 뜬다.

 

어노테이션에 등록 되어있기 때문에 미리 파싱해서 sql로 들고있으려고 하는데 문법에 안맞네.ㅋ 오류내야지 이거임

 

대부분의 오류를 다 잡아준다.

 

그렇다고 네임드 쿼리 몇백줄씩 하면 안되겠지?

 

 

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

 

그리고 xml에 정의할 수도 있다.

 

xml이 항상 우선권을 가진다.

 

애플리케이션 운영 환경에 따라 다른 xml을 배포할 수 있다.

 

 

 

뭐 근데...엔티티가 더러워지니까 좀 별로긴해

'Developer' 카테고리의 다른 글

[JPA실전]시작  (0) 2020.09.28
[JPA]벌크연산  (0) 2020.09.23
[JPA]쿼리에 Entity 직접 사용하기  (0) 2020.09.22
[JPA]다형성 JPQL 쿼리  (0) 2020.09.22
[JPA]BatchSize - 전역선언해서 사용하기  (0) 2020.09.22
  Comments,     Trackbacks
[JPA]쿼리에 Entity 직접 사용하기

select count(m) from member m

 

과 select count(m.id) from member m 은 같다.

 

똑같이 select count(m.id) from member m 으로 sql을 박는다.

 

 

 

그리고 파라미터로 엔티티 자체를 줄수도 있다.

 

select m from member m where m=:member

 

쿼리를 이렇게 짜고 .setParameter("member", member)

 

로 파라미터를 주면 이것 은 곧

 

select m from member m where m.id=:memberId

 

와 같다.

 

JPA 엔티티는 DB에 넘어갈때 PK로 넘어가게 된다.

 

당연하지만 흥미롭다.

'Developer' 카테고리의 다른 글

[JPA]벌크연산  (0) 2020.09.23
[JPA]JPQL-Named쿼리  (0) 2020.09.22
[JPA]다형성 JPQL 쿼리  (0) 2020.09.22
[JPA]BatchSize - 전역선언해서 사용하기  (0) 2020.09.22
[JPA]BatchSize - LAZY로딩 시 몇개를 가져올거냐?  (0) 2020.09.22
  Comments,     Trackbacks
[JPA]다형성 JPQL 쿼리

타입을 지정해서 뽑을수 있다.

 

 

 

다운캐스팅 같이 위처럼 사용도 할수 있다.

  Comments,     Trackbacks
[JPA]BatchSize - 전역선언해서 사용하기

쿼리가 N+1이 아니라 딱 테이블 수만큼 맞출수가 있다.

 

 

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.2"
xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">
<persistence-unit name="hello">
<properties>
<!-- 필수 속성 -->
<property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
<property name="javax.persistence.jdbc.user" value="sa"/>
<property name="javax.persistence.jdbc.password" value=""/>
<property name="javax.persistence.jdbc.url" value="jdbc:h2:tcp://localhost/~/test"/>
<property name="hibernate.dialect" value="dialect.MyH2Dialect"/>
<!-- 옵션 -->
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.use_sql_comments" value="true"/>
<property name="hibernate.hbm2ddl.auto" value="create" />
<property name="hibernate.default_batch_fetch_size" value="100"/>
</properties>
</persistence-unit>
</persistence>

  Comments,     Trackbacks
[JPA]BatchSize - LAZY로딩 시 몇개를 가져올거냐?

참 골때리는 놈이다.

 

 

String query = "select t from Team t";
List<Team> resultList = em.createQuery(query, Team.class).
setFirstResult(0).setMaxResults(2).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);
}
}

 

아래는 Team의 Member 리스트 선언.

 

@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();

 

만약 이대로 실행하면 맨처음 Team 가져오는 select 쿼리때리고 지연로딩이니까 프록시 가져온 다음,

 

루프 안에서 팀 개수대로 Member select 쿼리를 조질것이다.

 

그럼 결국 총 2번의 member select 쿼리를 조지지.

 

근데 이 조짐의 대상의 수를 조절할수가 있다.ㅋㅋ

 

방법은 BatchSize를 쓰는것이다.

 

@BatchSize(size = 2)
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();

 

이렇게 한번에 몇개를 조질지 사이즈를 정하면 한방에 몇개를 가져오느냐를 정할수 있다.

 

사이즈를 2로 했으니 2번 할 작업을 한번만 할거다.

 

실행하면 루프안에서 select 한번만 때린다.

 

사이즈를 1로 하면 어차피 없는거나 다름없으므로 역시 2번을 때린다.

'Developer' 카테고리의 다른 글

[JPA]다형성 JPQL 쿼리  (0) 2020.09.22
[JPA]BatchSize - 전역선언해서 사용하기  (0) 2020.09.22
[JAP]fetch 조인 - 특징과 한계  (0) 2020.09.22
[JPA]JPQL - distinct 중복제거  (0) 2020.09.22
[JAP]fetch 조인 - 한방쿼리  (0) 2020.09.22
  Comments,     Trackbacks
[JAP]fetch 조인 - 특징과 한계

연관된 엔티티들을 SQL 한번으로 조회 - 성능 최적화

 

LAZY 전략보다 우선시 되어 한방에 DB에서 뽑아온다.

 

실무에서는 글로벌 로딩 전략은 모두 지연로딩(LAZY 로딩) 으로 잡고,

 

최적화가 필요한 곳에 이 fetch 조인은 적용한다. (성능 문제 대부분 해결)

 

 

 

fetch 조인 대상에게는 별명을 줄수 없다.

 

근데 하이버네이트는 가능하긴 한데 사용하지 말자.

 

어차피 다 조회하는 한방쿼리인데 그 대상의 속성들에게 별명을 줄 필요도 없고 준다해도 위험함.

 

 

 

그리고 둘 이상의 컨렉션은 fetch 조인을 할수 없다.

 

 

컬렉션을 fetch 조인 하면 페이징을 사용할수가 없다.

 

일대일, 다대일 같은 단일 값 연관 필드들은 fetch 써도 페이징 가능.

 

데이타가 뻥튀기 되기 때문에 컬렉션을 fetch 조인하면 페이징을 절대 못함.

 

일대다 를 뒤집어서 다대일로 바꿔서 페이징 구현할 수도 있음.

 

작동은 해도 개판되는거임. 망함. (하이버네이트는 경고로그를 남기고 메모리에서 페이징함,겁나위험함)

 

 

 

  Comments,     Trackbacks