JPA - 영속성 컨텍스트

조은지·2022년 3월 18일
0

JPA

목록 보기
1/1

자바 ORM 표준 JPA 프로그램 강의

간단한 예제

@Column의 insertable 속성을 테스트해보다가 영속성 컨텍스트 관련해서 예제를 만들어보았다.

강의 예제와 비슷하지만 생겼던 문제를 해결해보며 영속성 컨텍스트를 이해해할 수 있었다.

Member 객체

@Entity
public class Member {
    @Id
    private Long id;

    @Column(name = "name",insertable = false)
    private String username;

    private Integer age; //Integer와 가장 적합한 데이터타입이 설정된다.

    @Enumerated(EnumType.STRING)
    private RoleType roleType; //DB에는 Enum이 없음 -> Enumnerated

    @Temporal(TemporalType.TIMESTAMP)
    private Date createdDate; //날짜와 같은 속성일 때는 Temporal

    @Temporal(TemporalType.TIMESTAMP)
    private Date lastModifiedDate;

    @Lob
    private String description; //varchar을 넘어서는 텍스트 정보를 넣고 싶으면 Lob을 사용한다.

    public Member(){
    }

    public Member(Long id, String username) {
        this.id = id;
        this.username = username;
    }
    
	public void getUsername() {
        return username;
    }
    
    public void setUsername(String username) {
        this.username = username;
    }
}

1. persist 후 값 변경

 public static void main(String[] args) {
        //persistence unit name을 적어야 한다.
        EntityManagerFactory enf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = enf.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        tx.begin();
        //code
        try {
            Member member1 = new Member(154L, "AAA");
            Member member2 = new Member(164L, "BBB");

            em.persist(member1);
            em.persist(member2);
            member1.setUsername("Change");

            System.out.println(em.find(Member.class,154L).getUsername());

            System.out.println("================================");
            tx.commit();
        }catch(Exception e){
            tx.rollback();
        }finally {
            em.close();
        }
        enf.close();
    }

=> insert 쿼리와 update 쿼리가 같이 나간다.


비영속 상태

Member member1 = new Member(154L, "AAA");
Member member2 = new Member(164L, "BBB");

영속 상태

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

=> 1차 캐시와 쓰기지연 SQL 저장소에 저장이 된다.
usernam의 insertable은 쿼리 로그 부분에서 설명


영속 상태 - 변경 감지

member1.setUsername("Change");

=> 영속성 컨텍스트에서 member1의 변경을 감지하고, 쓰기지연 SQL저장소에는 update문을 날린다.

System.out.println(em.find(Member.class,154L).getUsername());

member1을 그대로 가져오는 것이 아니라 영속성 컨텍스트를 관리하는 엔티티매니저에서 객체를 가져오고 이름을 출력했을 때 "Change"로 출력이 되는 것을 볼 수 있다.


트랜잭션 커밋 - 플러시

tx.commit();

트랜잭션 커밋 시 플러시가 발생한다.

JPA에서 플러시

  • 영속성 컨텍스트의 변경 내용을 DB에 반영하는 것이다.
  • 쓰기 지연 SQL저장소에 저장되었던 쿼리들을 한 번에 전달한다.
  • 영속성 컨텍스트를 비우는 것이 아니라 동기화를 해주는 개념!

콘솔 - 쿼리

Hibernate: 
    /* insert hellojpa.Member
        */ insert 
        into
            Member
            (age, createdDate, description, lastModifiedDate, roleType, id) 
        values
            (?, ?, ?, ?, ?, ?)
Hibernate: 
    /* insert hellojpa.Member
        */ insert 
        into
            Member
            (age, createdDate, description, lastModifiedDate, roleType, id) 
        values
            (?, ?, ?, ?, ?, ?)
Hibernate: 
    /* update
        hellojpa.Member */ update
            Member 
        set
            age=?,
            createdDate=?,
            description=?,
            lastModifiedDate=?,
            roleType=?,
            name=? 
        where
            id=?

insert 문이 나간 후 update문이 작성되었다.


1번 예제 특이점?

  • Member의 username을 insertable=false로 하고
  • Member = new Member(id값,"username")으로 했을 때
Member member1 = new Member(154L, "AAA");
Member member2 = new Member(164L, "BBB");

em.persist(member1);
System.out.println(em.find(Member.class,154L).getUsername());
em.persist(member2);

persist 후 member1의 username을 출력하면 "AAA"가 나온다

영속성 컨텍스트 내의 1차 캐시에는 "AAA"를 같이 저장해주기 때문


그러나 DB를 보면?

AAA값이 들어가있지 않음을 확인할 수 있다.

tx.commit();
System.out.println(em.find(Member.class,154L).getUsername());

트랜잭션 커밋 후에 출력을 해보아도 객체 값을 1차캐시에서 가져오기 때문에 "AAA"가 출력이 된다.


null 출력을 제대로 받으려면?

em.detach(member1);

System.out.println(em.find(Member.class,154L).getUsername());

준영속 상태로 만들었다가 다시 DB에 가져와야지만 null값이 제대로 나온 것을 확인할 수 있다.


1번 예제 정리

  • insertable는 거의 사용하지 않는다고 했다!
  • 영속성 컨텍스트에 영향을 주는 컬럼은 삽입 시 조심해야한다!!
    => 너무 당연한 말 같은데 username을 삽입 시 넣지 않겠다고 했으면서 객체 생성 시 username값을 넣어줘서 생긴 문제기 때문에 객체 생성 시 username 값을 넣어주지 않는 방향으로 구현해야한다!

2. 값 변경 후 persist

Member member1 = new Member(154L, "AAA");
Member member2 = new Member(164L, "BBB");
member1.setUsername("Change");

em.persist(member1);
System.out.println(em.find(Member.class,154L).getUsername());

너무 당연한 예제라는 것을 예제1을 해보고서야 알게 되었다.

Member member1 = new Member(154L, "AAA");
Member member2 = new Member(164L, "BBB");
member1.setUsername("Change");

이 부분이 모두 비영속 상태이기 때문에 영속성 컨텍스트에 아무런 영향을 끼치지 않는다.

  • 이 때도 마찬가지로 콘솔 창에는 "Change"가 뜨지만 실제 DB에서는 null값이 들어가있음

콘솔- 쿼리

Hibernate: 
    /* insert hellojpa.Member
        */ insert 
        into
            Member
            (age, createdDate, description, lastModifiedDate, roleType, id) 
        values
            (?, ?, ?, ?, ?, ?)
Hibernate: 
    /* insert hellojpa.Member
        */ insert 
        into
            Member
            (age, createdDate, description, lastModifiedDate, roleType, id) 
        values
            (?, ?, ?, ?, ?, ?)

비영속 상태에서 값이 바뀌고 영속상태가 된 것이기 때문에 update문은 날라가지 않는다.


코드

 Member member1 = new Member(154L, "AAA");
Member member2 = new Member(164L, "BBB");
member1.setUsername("Change");

Member findMember = em.find(Member.class, 154L);
System.out.println(findMember);

이렇게 비영속 상태에서 find를 하게 되면 1차캐시에 값이 없기 때문에
즉시 쿼리를 날리게 되고 findMember는 null값이 리턴된다.

insertable 테스트 해보다가 영속성컨텍스트 복습한 예제였다..

0개의 댓글

관련 채용 정보