본 내용은 인프런의 자바 ORM 표준 JPA 프로그래밍 - 기본편 강의를 수강하고 정리한 글입니다.
위의 그림을 보고 설명하자면,
먼저 JPA는 Persistence 클래스가 존재한다. 이 클래스는 개발자가 작성한 persistence.xml에서 설정 정보를 읽어와 EntityManagerFactiory라는 클래스를 만든다.
이렇게 만든 EntityManagerFactory에서는 뭔가 필요할 때마다 EntityManager를 찍어내 만든다.
public class JpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
em.close();
emf.close();
}
}
해당 코드를 처음부터 자세히 확인해보자.
public class JpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("")
}
Persistence라는 클래스의 createEntityManagerFactory()
를 통해서 EntityManagerFactory를 반환해 준다.
여기에서 createEntityManagerFactory()안에는 PersistenceUnitName을 넘기라고 한다.
이것은 persistence.xml 파일의 persistence-unit에 설정한 name을 넘겨주면 된다.
public class JpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
em.close();
emf.close();
}
}
다음은 EntityManagerFactory에서 createEntityManager()를 꺼낸다.
실제 애플리케이션이 끝나면 EntityManagerFactory를 닫아주기 위해서 close();
해준다.
위의 내용을 코드를 통해 확인해보자!
데이터베이스에 Member 테이블을 생성해주고, JPA에 엔티티를 추가해준다.
그리고 아래와 같이 member를 생성해준다.
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
Member member = new Member();
member.setId(1L);
member.setName("HelloA");
em.persist(member);
em.close();
emf.close();
}
하지만 이렇게 실행할 경우 에러가 발생하는데, JPA는 데이터를 변경하는 작업은 반드시 Transaction 안에서 작업해야 하기 때문이다.
여기서 중요한 것은 엔티티 매니저 팩토리는 애플리케이션 로딩 시점에 하나만 생성하여야 하고, 트랜잭션 단위는 DB 커넥션을 얻어 쿼리를 날리고 종료되는 단위마다 엔티티 매니저를 통해 실행된다. 간단하게 생각하자면 엔티티 매니저는 DB 커넥션이라고 할 수 있다.
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Member member = new Member();
member.setId(1L);
member.setName("HelloA");
em.persist(member);
tx.commit();
em.close();
emf.close();
}
getTransaction()
으로 트랜잭션을 얻는다.begin()
- 트랜잭션 시작em.persist(member)
로 member를 저장tx.commit()
- 커밋
쿼리와 데이터베이스에 helloA 멤버가 저장된 것을 확인할 수 있다.
또한 이것으로 확인 할 수 있는 것은, 내가 직접 쿼리를 작성할 필요 없이, JPA가 매핑정보를 통해 모든 것을 해준다는 것이다.
위와 같이 코드를 작성할 경우 문제 발생 가능성이 존재한다.
만약 em.persist(member);
나 tx.commit();
시점에 문제가 생기게 되면, EntityManager close()
와 EntityManagerFactory close()
가 모두 호출되지 않기 때문이다.
따라서 try-catch 문을 통해 문제가 일어났을 경우에도 코드가 올바르게 동작하도록 수정해주어야 한다.
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(2L);
member.setName("HelloB");
em.persist(member);
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
}
emf.close();
}
commit()
한다. rollback()
한다.close()
해준다.이렇게 JPA의 동작에 대해서 알아보았다. 하지만 실제로는 이렇게 EntityManager를 생성할 필요 없이 spring이 이러한 것을 해주기 때문에 호출해서 더욱 편하게 사용할 수 있다.
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
Member findMember = em.find(Member.class, 1L);
System.out.println("찾은 회원 이름 : "+ findMember.getName());
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
}
emf.close();
}
select 쿼리로 회원을 찾은 것을 볼 수 있다.
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
Member findMember = em.find(Member.class, 1L);
em.remove(findMember);
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
}
emf.close();
}
remove(); 메소드 안에 삭제할 멤버를 넣어주면 Delete 쿼리가 나가면서 삭제된다.
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
Member findMember = em.find(Member.class, 1L);
findMember.setName("HelloJPA");
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
}
emf.close();
}
set() 메서드로 변경할 이름으로 바꾸어준다.
여기서 em.persist로 변경한 이름을 저장해주어야 하는게 아닌가? 라는 생각을 할 수도 있지만, 저장하지 않아도 된다.
쿼리를 확인해보면 UPDATE 쿼리가 나간 것을 볼 수 있다.
데이터베이스에 변경한 이름인 helloJPA로 이름이 변경된 것을 확인했다.
그렇다면 저장하지 않았음에도 어떻게 해서 이름을 변경할 수 있는 것일까?
그 이유는 JPA를 통해서 엔티티를 가져오게 되면, 가져온 엔티티는 JPA가 관리를 하게 된다. 이후 트랜잭션 커밋 시점에 관리되는 엔티티는 변경 사항을 체크한다. 트랜잭션이 커밋하기 직전에 UPDATE 쿼리를 실행해 변경사항을 수정하고 트랜잭션이 커밋된다.