JPA 강의 듣기 시작!
우선 JPA를 왜쓰는지에 대해 알아보자. 우리는 객체지향 언어를 다루고 있다. 그리고 그 객체를 저장하고 싶다. 하지만 RDB에는 객체지향의 핵심 가치인 상속을 반영하지 않는다. 물론 연관관계를 가지고 있지만 그렇다고 상속과 똑같이 작동하지 않는다.
예를 들어보자. 자바의 컬렉션에서 그래프 탐색을 할 때 그냥 탐색을 하면 된다.
ex)
member.getA().getB().getC();
하지만 테이블은 이런식으로 조회할 때 어려움이 있다. JOIN문을 이용할 때 처음 실행하는 쿼리문에 따라 탐색 범위가 결정된다.
JPA가 아닌 기존의 JDBC와 같은 방식을 이용해 객체를 찾아오면 같은 객체를 조회한다고 해도 SELECT를 이용해 가져올 때 각 객체를 다르게 가져오게 된다.
String memberId = "100";
Member member1 = memberDAO.getMember(memberId);
Member member2 = memberDAO.getMember(memberId);
member1 == member2 -> 다름!!!
이러한 문제 때문에 객체지향적인 프로그래밍을 할경우 DB와 연동이 될 때 더 복잡해지는 경우가 생긴다.
JPA는 이러한 패러다임의 불일치를 해소시켜주는 역할을 한다. 어떻게? DB를 마치 JAVA의 컬렉션처럼 사용할 수 있도록.
JPA는 자바 ORM의 표준명세. 그중 우리는 Hibernate를 이용하게 될 것. 가장 많이 쓰인다고 한다.
JPA는 App과 JDBC사이에서 역할을 해준다. Java App과 JDBC간의 패러다임 불일치를 해결해주는 연결다리 역할!
추가적으로 JPA가 좋은 점으로는 SQL방언에 강하다. SQL 방언이랑 MySQL과 Oracle은 다른 쿼리를 쓴다. 하지만 완전히 다른게 아니라 공통적인부분 90프로와 10프로의 다른부분이 있는데, 이 10프로 때문에 골치아파진다.
예를들어 내가 DB를 MySQL에서 Oracle로 변경할 때, 모든 쿼리를 다 바꾼다 생각해보자. JPA는 이를 극복해준다. 어떻게? JPA는 쿼리를 직접 작성하는 게 아니라, SQL을 이렇게 작성하라고 지시하기 때문에, 설정파일만 MySQL -> Oracle만 변경해주면 쿼리를 직접 수정하지 않아도 알아서 수정해서 날려준다.
딱봐도 유지/보수에 엄청난 이득을 준다. 이뿐아니다.
Table에 컬럼을 하나 추가할 때 우리는 SQL로 Table을 업데이트 해줘야 한다. 하지만 그럴 필요가 없다.
JPA를 사용한다면 Entity 클래스에 필드값만 추가해주면 JPA가 알아서 컬럼을 추가해준다.
JPA는 위에서 언급했던
String memberId = "100";
Member member1 = memberDAO.getMember(memberId);
Member member2 = memberDAO.getMember(memberId);
이 두 객체 member1==member2는 일치하게 해준다. JPA를 사용하면 DB를 마치 컬렉션처럼 사용이 가능하게 되는 것.
이는 같은 트랜잭션 안에서만 발생이 가능하다.
실전으로 가보자.
JPA를 이용해 객체를 DB에 저장해보자.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
"hello"라는 DB의 EmtityManagerFactory를 생성해주자. 앞으로 EmtityManagerFactory는 EntityManager를 관리해준다.
이 EntityManager는 App과 DB를 연결해주는 커넥션 으로서 작동한다.
Entity를 하나 저장하는 코드.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
//code
Member member = new Member(); // 저장할 멤버 객체 생성
EntityTransaction tx = em.getTransaction(); // EntityManager가 Transaction을 걸기 위해 생성
tx.begin();
try{ // 만약 트랜잭션이 실패될 때 처리를 위해.
member.setId(1L);
member.setName("태호");
//em.persist()로 저장준비를 한다.
em.persist(member);
// commit은 트리거.
tx.commit();
}catch (Exception e){
//만약 위에서 실패가 일어나면 transaction을 롤백해준다.
tx.rollback();
}finally {
//그리고 EntityManager를 닫아주면서 커넥션풀을 반환함.
em.close();
}
em.close();
emf.close();
Entity 조회 및 수정 코드
package hellojpa;
import jakarta.persistence.*;
public class JpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
//code
Member member = new Member();
EntityTransaction tx = em.getTransaction();
tx.begin();
try{
//EntityManager야~ Id값이 1L 찾아와서 Member 클래스로 매핑해!
Member findeMember = em.find(Member.class,1L);
//찾아온 엔티티의 Id를 2L로 바꿔!
findeMember.setId(2L);
// 저장 준비
em.persist(member);
//트리거!
tx.commit();
}catch (Exception e){
tx.rollback();
}finally {
em.close();
}
em.close();
emf.close();
}
}
위의 것들은 모두 단건 조회다. 하지만 우리는 하나만 가져올게 아니라 여러 Entity를 가져오고 싶다.
그럴땐 JPQL을 이용해서 직접 쿼리를 작성한다. 근데 JPA의 쿼리니까 어떻게? 객체지향적으로. TABLE을 가져오는 게 아닌, Member Class로 매핑해서 가져오게끔!
package hellojpa;
import jakarta.persistence.*;
import java.util.List;
public class JpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
//code
EntityTransaction tx = em.getTransaction();
tx.begin();
try{
// 바로 이부분이 TABLE의 관점이 아닌 객체의 관점으로 가져오는것.
List<Member> memberList = em.createQuery("select m from Member as m",Member.class).getResultList();
for (Member member : memberList) {
System.out.println("member name is " + member.getName());
}
tx.commit();
}catch (Exception e){
tx.rollback();
}finally {
em.close();
}
em.close();
emf.close();
}
}
추가적으로 Transaction을 언제 어떻게 걸어야될지 잘 몰랐다. 근데 이 Transaction은 DB속 데이터가 바뀔 때 무조건 걸어주면 된다고 하신다. 아마 get을 제외한 모든 부분을 다 Transaction을 걸어야겠지? 데이터 삽입도 데이터 변경, 수정, 삭제 모두 데이터를 변경하게 되니까?