package hellojpa;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
public class JpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager entityManager = emf.createEntityManager();
// 실제 코드 작성 부분
entityManager.close();
emf.close();
}
}
create table Member (
id bigint not null,
name varchar(255),
primary key (id)
)
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;
}
}
package hellojpa;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
public class JpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try{
Member member = new Member();
member.setId(4l);
member.setName("test");
em.persist(member);
Member findMember = em.find(Member.class, 4l);
System.out.println("findMember.getId() = " + findMember.getId());
System.out.println("findMember.getName() = " + findMember.getName());
member.setName("testUpdate");
em.merge(member);
Member updatedMember = em.find(Member.class, 4l);
System.out.println("updatedMember.getId() = " + updatedMember.getId());
System.out.println("updatedMember.getName() = " + updatedMember.getName());
em.remove(member);
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
}
emf.close();
}
}
jdbc메소드를 통해 저장을 했으니 그 객체를 다시 불러온다는 취지로 em.find() 메소드를 사용하고, 이전에 저장했던 식별자인 4l 값을 파라미터로 전달했다. 그리고 그 객체를 findMember라고 다시 정의했다. 그리고 그 객체를 통해 getter를 사용해 엔티티 정보를 불러와 콘솔에 출력했다. 업데이트도 마찬가지로 merge() 로 데이터를 병합한 후 다시 find() 메소드로 updateMember 객체를 가져와 getter를 사용하길 반복했다.
<aside> ⚠️ 엔티티 매니저 팩토리는 하나만 생성해서 애플리케이션 전체에 공유한다. 엔티티 매니저는 thread 간에 공유X (사용하고 버려야 한다.) JPA의 모든 데이터 변경은 Transaction 안에서 실행된다.
</aside>
findMember.getId() = 4
findMember.getName() = test
updatedMember.getId() = 4
updatedMember.getName() = testUpdate
Hibernate:
/* insert hellojpa.Member
*/ insert
into
member
(name, id)
values
(?, ?)
Hibernate:
/* delete hellojpa.Member */ delete
from
member
where
id=?
<aside>
⚠️ 로그 확인 결과는 이상하다. println() 은 맨 위에서 나오고, 정작 그 사이사이에 있을 것으로 기대했던 SQL문들은 마지막에 모여있다. 로그의 위치도 이상하고, 분명 em.find()과 merge() 메소드를 사용했는데 어째서 SELECT, UPDATE SQL의 흔적은 로그에 남지 않았을까?
</aside>
사실 위 코드는 문법적으로는 별 문제가 없기 때문에 얼핏 정상적으로 작동하는 것처럼 보이지만, 이것은 JPA의 자동화에 의해 불필요한 호출이 “생략된” 것에 가깝다. 아래는 수정된 코드이다.
package hellojpa;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
public class JpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try{
Member member = new Member();
member.setId(4l);
member.setName("test");
em.persist(member);
// Member findMember = em.find(Member.class, 4l);
// System.out.println("findMember.getId() = " + findMember.getId());
// System.out.println("findMember.getName() = " + findMember.getName());
System.out.println("findMember.getId() = " + member.getId());
System.out.println("findMember.getName() = " + member.getName());
member.setName("testUpdate");
// em.merge(member);
// Member updatedMember = em.find(Member.class, 4l);
// System.out.println("updatedMember.getId() = " + updatedMember.getId());
// System.out.println("updatedMember.getName() = " + updatedMember.getName());
System.out.println("updatedMember.getId() = " + member.getId());
System.out.println("updatedMember.getName() = " + member.getName());
em.remove(member);
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
}
emf.close();
}
}
첫번째 코드에서 사용했던 find 메소드를 모두 뺐고, 업데이트를 위한 merge조차 없앴다. 업및 db에서 다시 데이터를 꺼내오는 과정까지도 생략해버렸다. 과연 로그는 어떻게 나올까?
findMember.getId() = 4
findMember.getName() = test
updatedMember.getId() = 4
updatedMember.getName() = testUpdate
Hibernate:
/* insert hellojpa.Member
*/ insert
into
member
(name, id)
values
(?, ?)
Hibernate:
/* delete hellojpa.Member */ delete
from
member
where
id=?
완전히 동일하다. 어떻게 이런 일이 가능할까? 이 작동 원리를 이해하려면 JPA가 가진 막강한 “캐시” 작동 방식을 이해해야 한다. 첫 시간에 어렴풋이 언급한 JPA의 장점을 구체적으로 확인할 수 있다.
이 캐시 기능에 대해서는 엔티티 관리방법 “캐시” 에서 알아보자.