Per ardua ad astra !
I'm On My Way
Per ardua ad astra !
전체 방문자
오늘
어제
  • 분류 전체보기 (126)
    • Algorithm (50)
      • 백준 (30)
      • SWEA (3)
      • JUNGOL (3)
      • Programmers (5)
      • LeetCode (2)
    • 안드로이드 개발 (6)
      • Java로 개발 (0)
      • Kotlin으로 개발 (3)
    • Spring (41)
      • Spring기본 (17)
      • JPA기본 (15)
      • JPA활용 SpringBoot 기본 (9)
      • API 개발 기본 (0)
    • 네트워크 (3)
    • 운영체제 (0)
    • Life (3)
      • 책 (0)
      • 자기계발 (1)
      • 일상 (2)
    • 노마드코더 (3)
      • python으로 웹 스크래퍼 만들기 (3)
    • 프로그래밍 언어 (17)
      • Java 기본 (2)
      • 코틀린 기본 (15)

블로그 메뉴

  • 홈
  • 방명록

인기 글

hELLO · Designed By 정상우.
Per ardua ad astra !

I'm On My Way

JPA 시작
Spring/JPA기본

JPA 시작

2021. 7. 8. 12:59

JPA 실행


1. DB서버와 잘 연결되는지 확인 

테스트할 JpaMain이라는 파일을 만들고 
EntityManagerFactory는 하나만 생성해서 애플리케이션 전체에서 공유.
EntityManager는 Thread간에 공유가 안되기 때문에 사용하고 그냥 버려야 한다. 
JPA의 모든 데이터 변경은 Trasaction 안에서 실행되어야 한다. 

public static void main(String[] args){
        // start
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();

        // code

        // end
        em.close();
        emf.close();

    }


을 입력하여 잘 실행되는지 확인 
Persistence와 EntityMangerFactory와 EntityManager들의 관계도를 기억하자 !


2. DB에 table 생성

create table Member (
  id bigint not null,
  name varchar(255),
  primary key (id)
);

 

3. 생성한 table과 매핑되는 Entity class 생성

@Entity -> JPA가 처음 로딩될 때 JPA를 사용하는 클래스구나를 인식(필수)

                       && 기본 생성자 필수 (final, 인터페이스, inner 사용x) && 속성 name이 있는데 기본값은 클래스 명 그대로 
@Id -> PrimaryKey가 무엇인지 JPA에게 명시해줌(선택이 아닌 필수)
@Table(name = "테이블명") => 테이블 명 명시가 필요할 때(DB내의 테이블 명칭)
@Column(name = "콜롬명") => 콜롬명 명시가 필요할 떄(DB내의 콜럼명. Not 자바내의 객체 속성명)

package hellojpa;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Member {

    @Id
    private Long id;
    private String name;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}


4. 만든 클래스의 객체 인스턴스를 생성하고 DB에 넣기, 조회, 삭제, 수정

 

**JPA는 DB수정과 관련된 모든 트랜잭션 단위의 행동이 중요하다.**

(1) 데이터 추가

public class JpaMain {

    public static void main(String[] args) {
        // start
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();

        EntityTransaction tx = em.getTransaction(); // 트랙잭션
        tx.begin();

        // code
        try {
            Member member = new Member();
            member.setId(2L);
            member.setName("HelloB");
            em.persist(member);
            tx.commit();

        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close(); // 사용 다하면 꼭 닫아줘야됨
        }
        // end
        emf.close();
    }
}

 

persist()를 하면 먼저 1차 캐시에 데이터가 


(2) 조회, 삭제, 변경

	// code
        try {
            // 조회
            Member member = em.find(Member.class, 1L);
            System.out.println("member.Id = " + member.getId());
            System.out.println("member.name = " + member.getName());

            // 삭제
            em.remove(member);

            // 변경
            member.setName("HelloJPA");

            tx.commit();

        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close(); // 사용 다하면 꼭 닫아줘야됨
        }


변경이 진짜 신세계였습니다....

자바 객체에서 값만 바꿨는데 어떻게 바뀌지??

 

=> JPA를 통해서 엔티티를 가져오면 JPA가 알아서 관리를 해주는데, 저장되거나 조회되는 데이터 값을 영속성 컨텍스트에서 임시로 저장을 해둔다. 그리고 만약 이 데이터를 추가하거나 수정하면 쓰기지연 SQL 저장소에 해당쿼리가 저장되었다가,  flush()하는 시점(거의 commit()할 때)에 쿼리가 처리가 된다.

만약 데이터가 변경된 상태라면 변경된 부분은 모두 update 쿼리를 알아서 짜서 날린다고 합니다.ㄷㄷ(변경감지 Dirty Checking)

DB에 없어서 추가해야되는 상태면 insert 쿼리를 짜서 알아서 날린다고 한다...ㄷㄷㄷ

 


JPQL(Java Persistence Query Language) 


JPA에서 제공하는 메소드 호출만으로 섬세한 쿼리 작성의 한계점을 느낌(ex) 세세한 변경 혹은 전체 조회 등)

ex) 사용법
List<Member> resultList = em.createQuery("select m from Member as m", Member.class).getResultList();

JPQL은 절대 table을 위주로 코드를 짜지 않는다. Member 엔티티 객체를 위주로 코드가 짜여짐. => ex) * 문자가 아닌 m
-> 장점: 어떤 DB 방언이 오든 관계가 없이 JPQL로 커버 가능하다는 점. 객체 지향 SQL이라고 이해하자. 

 

영속성 컨텍스트

 

영속성 컨텍스트는 동일성(identity)를 보장하고 트랜잭션을 지원하는 쓰기 지연, 변경감지, 지연로딩을 위한 논리적인 개념이다. 1차 캐시도 이에 포함된다. 

 

상태 및 생명주기

영속: 영속성 컨텍스트에서 관리되는 상태이다. (persist, find 등을 통해서 데이터는 1차 캐시로 들어와 있는 상태가 됨)

비영속: 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태이다. 

준영속(detached): 영속성 컨텍스트에 저장되었다가 분리된 상태이다. (detached(), clear, close())

삭제(removed): removed에 의해 삭제된 상태이다. 

 

1차 캐시를 이해하기 위해 예를 들어보자

	try {
            Member member1 = em.find(Member.class, 1L);
            Member member2 = em.find(Member.class, 1L);
            if(member1 == member2){
                System.out.println("둘은 같아요 !");
            }
            tx.commit();
        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close(); // 사용 다하면 꼭 닫아줘야됨
        }

결과값

이 코드를 실행하면 DB에 쿼리를 날려 id가 1L에 해당하는 데이터를 먼저 찾고 가져와서 캐시1에 저장하고 그 값은 member1에 저장된다. 다음코드도 똑같이 member2에 적용했다. 해당 데이터는 이미 DB에서 조회 했었기 때문에 DB에 쿼리를 날려 접근하지 않고 캐시1을 뒤져서 똑같은 데이터가 있는지 찾는다. (이때, id(기본키)는 키가 된다 - 해시맵으로 이해). 만약 데이터가 있다면 그걸 그대로 가져와 member2에 저장한다. 

따라서, member1과 member2는 같은 데이터이고, 날린 쿼리는 1개밖에 출력되지 않음을 확인할 수 있다.

 

flush()

 

flsuh() => 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영하는 것을 의미합니다. 

플러시 호출방법:

1) em.flush() -> 직접호출 (많이 안쓰임)

2) transaction.commit() ->트랜잭션으로 커밋하면 플러시 자동호출(가장 보편적인 플러시 방법) 

3) JPQL 쿼리 실행 -> 플러시가 자동 호출됨. 이유는 JPQL은 현재 DB에 접근해서 쿼리를 날리는데 영속성 컨텍스트의 변경점이 전혀 반영되어 있지 않은 DB의 상태이기 때문에 플러시를 먼저하고 쿼리가 실행되도록 하는 것이다.

 

flush가 발생하면, JPA는 알아서 변경감지(Drity Checking)를 하고 수정된 엔티티 쓰기지연 SQL 저장소에 등록했다가 그 쿼리를 DB에 전송해서 DB를 영속성컨텍스트의 변경점대로 수정해준다. 즉, 영속성 컨텍스트의 변경내용을 데이터베이스에 동기화 시켜준다 !

 

 

3)의 JPQL 쿼리 실행을 예로 들어보면

 

현재 아래의 Member table 상태에서

Member table 상태

아래 코드를 실행하는 경우 

package hellojpa;

import org.hibernate.Transaction;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import java.util.List;

public class JpaMain {

    public static void main(String[] args) {
        // start
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();

        EntityTransaction tx = em.getTransaction(); // 트랙잭션
        tx.begin();

        // code
        try {
            Member member = em.find(Member.class, 2L);
            member.setName("HelloI'mB");
            System.out.println("member.name = " + member.getName());

            List<Member> resultList = em.createQuery("select m from Member as m", Member.class).getResultList();
            for (Member member1 : resultList) {
                System.out.println("member.name = " + member1.getName());
                System.out.println("member.id = " + member1.getId());
            }
            System.out.println("===============");
            tx.commit();
        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close(); // 사용 다하면 꼭 닫아줘야됨
        }
        // end
        emf.close();
    }
}

 

결과는 다음과 같다

try문에서 find 메소드에 따라 먼저 쿼리를 날려 데이터를 찾는다. 그럼 해당 데이터는 영속성 컨텍스트에 저장이 되어 있는 상태이다. 그다음 set메소드로 값을 변경 시켜줬는데 데이터 변경에 관한 쿼리는 쓰기 지연에 의해 나중에 쿼리가 날려져서 member.name이 먼저 출력된다. 말했던 것과 같이 보통 쓰기 지연된 상태에서 변경감지가 있어 update쿼리를 나중에 날려야 되지만, 바로 다음 코드가 JPQL 이므로 flush()처리되어 업데이트 쿼리가 날려지고, 그다음 JPQL 쿼리가 날려진 모습이다. JPQL의 조회 이후 출력 코드는 순차적으로 실행된다.

만약, JPQL코드가 없었으면 "=========="이 먼저 출력되고 나중에 변경감지와 쓰기지연에 의해 update 쿼리가 날려졌을 것이다. 

 

 

이 게시물은 '자바 ORM 표준 JPA 프로그래밍' 강의를 수강하고 정리한 내용임을 밝힙니다. 
출처: https://www.inflearn.com/course/ORM-JPA-Basic#

 

자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런 | 강의

JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다., 본 강의는 자바 백엔

www.inflearn.com

 

    'Spring/JPA기본' 카테고리의 다른 글
    • 상속 관계 매핑 (Inheritance), 정보 상속(MappedSuperclass)
    • 연관관계 매핑 기초(단방향, 양방향), 다양한 연결관계 매핑
    • 자동 DDL 쿼리생성, 엔티티 매핑을 위한 기본키, 콜롬 어노테이션
    • JPA란? JPA 장점, JPA 프로젝트 설정
    Per ardua ad astra !
    Per ardua ad astra !
    개발자 지망생이며 열심히 공부하고 기억하기 위한 블로그입니다.

    티스토리툴바