자바 ORM 표준 JPA 프로그램 강의
@Column의 insertable 속성을 테스트해보다가 영속성 컨텍스트 관련해서 예제를 만들어보았다.
강의 예제와 비슷하지만 생겼던 문제를 해결해보며 영속성 컨텍스트를 이해해할 수 있었다.
@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;
}
}
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문이 작성되었다.
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값이 제대로 나온 것을 확인할 수 있다.
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");
이 부분이 모두 비영속 상태이기 때문에 영속성 컨텍스트에 아무런 영향을 끼치지 않는다.
콘솔- 쿼리
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 테스트 해보다가 영속성컨텍스트 복습한 예제였다..