JPA

JPA

dev-rootable 2023. 8. 11. 20:50

📌 ORM(Object-relational mapping) 기술

 

객체는 객체대로 관계형 DB는 관계형 DB대로 설계하고, ORM 프레임워크가 중간에서 매핑해 주는 식으로 동작하는 객체와 관계형 DB를 매핑해 주는 기술이다.

 

JPA 동작

 

JPA는 애플리케이션과 JDBC 사이에서 동작하며, 객체를 다루듯이 DB 리소스를 다룰 수 있도록 한다.

 

🔎 예제

 

JPA에서 하나의 테이블을 Entity 객체라고 하며, 아래와 같이 자바 코드를 통해 하나의 테이블을 설계할 수 있다.

 

@Entity
@Data
public class Member {
    
    @Id
    @GeneratedValue
    @Column(name = "member_id")
    private Long id;
    
    @Column(name = "USERNAME")
    private String name;
    
}

 

 

그리고 아래와 같이 EntityManager를 통해 persist()를 하면 위 그림처럼 Entity 분석부터 패러다임 불일치까지 해결해 준다.

 

//em == EntityManager
Member member = new Member();
member.setName("member1");
em.persist(member);

em.flush();

Member findMember = em.find(Member.class, member.getId());
System.out.println("findMember.name = " + findMember.getName());

 

아래 결과처럼 INSERT 쿼리가 자동으로 나간 것을 알 수 있다.

 

로그 - INSERT문

 

H2 저장 결과(저장하지 않은 필드는 null 처리)

 

📌 JPA를 사용하는 이유

 

JPA는 ORM 기술을 사용한 자바 진영의 표준 인터페이스다. 구현체로 Hibernate, EclipseLink, DataNucleus가 있는데, Hibernate가 가장 범용적으로 사용된다.

 

🔎 생산성

 

JPA는 다음과 같은 명령어 한 줄로 CRUD 작업을 대체한다.

 

  • 저장 ➡ em.persist(member)
  • 조회 ➡ Member member = em.find(Member.class, member.getId())
  • 수정 ➡ member.setName("newMember")
  • 삭제 ➡ em.remove(member)

 

기본 CRUD 함수로 해결되지 않는 쿼리는 JPQL이라는 SQL을 추상화한 객체 지향 쿼리 언어로 해결한다.

 

🔎 유지보수

 

SQL 중심적인 개발은 필드 한 개를 추가하더라도 모든 SQL을 수정해야 한다.

 

 

하지만 JPA는 코드에 필드 하나만 추가하면 SQL은 JPA가 처리한다.

 

@Entity
@Data
public class Member {
    
    @Id
    @GeneratedValue
    @Column(name = "member_id")
    private Long id;
    
    @Column(name = "USERNAME")
    private String name;
    
    @Column(name = "PHONE_NUM")
    private String tel;
    
}

 

🔎 패러다임 불일치 해결

 

✔ 상속

 

 

상속 관계의 객체라도 저장과 조회를 간편하게 할 수 있다.

 

em.persist(album); //INSERT INTO ITEM ... + INSERT INTO ALBUM ... 처리
Album album = em.find(Album.class, album.getId()); //JOIN문 처리

 

✔ 연관 관계

 

다음 코드로 회원과 팀 엔티티를 함께 저장할 수 있다.

 

Team team = new Team();

... //값 바인딩

member.setTeam(team); //연관 관계 설정
em.persist(member); //저장

 

✔ 객체 그래프 탐색

 

참조 값을 갖고 있기 때문에 연관 관계 엔티티에서 프로퍼티 함수로 자유롭게 객체 그래프를 탐색할 수 있다.

 

@Entity
@Data
public class Member {
    
    @Id
    @GeneratedValue
    @Column(name = "member_id")
    private Long id;
    
    @Column(name = "USERNAME")
    private String name;
    
    @Column(name = "PHONE_NUM")
    private String tel;
 
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "team_id")
    private Team team; //참조 값
 
}

 

Member member = em.find(Member.class, member.getId());
Team team = member.getTeam();

 

✔ 비교하기

 

동일한 트랜잭션에서 조회한 엔티티는 같음을 보장

 

String memberId = "100";
Member member1 = em.find(Member.class, memberId);
Member member2 = em.find(Member.class, memberId);

member1 == member2; //같다

 

📌 JPA 구동 방식

 

JPA 구동 방식

 

1. Persistence 클래스에서 META-INF/persistence.xml 을 읽은 후 사용할 DBMS나 DataSource 값 등을 셋팅한다.

 

<persistence-unit name="hello">
        <properties>
            <!-- 필수 속성 -->
            <property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
            <property name="javax.persistence.jdbc.user" value="sa"/>
            <property name="javax.persistence.jdbc.password" value=""/>
            <property name="javax.persistence.jdbc.url" value="jdbc:h2:tcp://localhost/~/test"/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
        </properties>
    </persistence-unit>

 

2. Persistence는 위 내용을 토대로 EntityManagerFactory를 생성한다.

 

3. EntityManagerFactory는 JPA가 필요할 때마다 EntityManager를 생성하여 쿼리를 수행한다.

 - DB 커넥션을 받아서 쿼리를 날리고 종료되는 단위의 작업을 할 때마다 EntityManager를 생성하여 작업해야 한다.

 

참고로 Jar 에서 Spring Data JPA 의존성을 받은 경우, 스프링으로부터 EntityManager를 바로 주입 받아 사용할 수 있다.
단, application.properties에서 DataSource 관련 셋팅은 해야 한다.

 

//War + Not Spring Container
public class Jpa {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");

        EntityManager em = emf.createEntityManager();

        EntityTransaction tx = em.getTransaction(); //트랜잭션 생성
        tx.begin(); //트랜잭션 시작

        try {
            ...
            tx.commit();
        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close();
        }

        emf.close();
    }
}

 

대부분 사용하는 Spring boot + Gradle 환경에서 개발하는 경우, 위처럼 main에서 모두 처리하지 않는다.
일반적으로 Service 계층에서 @Transactional 을 통해 트랜잭션을 열고 비즈니스 로직 등을 수행한 후 데이터 접근 계층에서
EntityManager를 주입 받아 쿼리를 수행하는 식으로 사용한다. 

 

 

🔎 주의할 점

 

1. EntityManagerFactory는 웹 서비스 당(DB 당) 1개만 생성해서 공유한다.

 

2. EntityManager는 스레드 간에 공유되면 안된다. 즉, 사용하고 바로 반납해야 한다.

 

💡 EntityManagerFactory vs EntityManager

EntityManagerFactory는 Thread Safe하여 여러 스레드가 동시에 접근해도 안전하므로 서로 다른 스레드 간에 공유해서 사용할 수 있다.

반면, EntityManager는 Thread Safe하지 않아 동시성 문제가 발생하므로 스레드 간에 절대 공유하면 안된다. 그래서 일반적으로 EntityManager를 EntityManagerFactory를 이용하여 생성한 것을 사용하지 않고 스프링에서 관리하는 EntityManager를 의존성 주입 받아 사용한다.
그 이유는 스프링에서 알아서 Proxy로 감싼 EntityManger를 생성하여 주입해주기 때문에 Thread Safe를 보장한다.

Reference:
https://wan-blog.tistory.com/30
 

EntityManagerFactory, EntityManager, PersistenceContext란?

EntityManagerFactory, EntityManager, PersistenceContext란? EntityManagerFactory란 데이터베이스를 하나만 사용하는 애플리케이션들은 일반적으로 EntityManagerFactory를 한개만 생성한다. 이 엔터티 매니저 팩토리로

wan-blog.tistory.com

 

3. JPA에서 모든 작업은 트랜잭션 안에서 수행되어야 한다.

 

Reference:

https://www.inflearn.com/course/ORM-JPA-Basic/dashboard

 

자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런 | 강의

초급자를 위해 준비한 [웹 개발, 백엔드] 강의입니다. JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자

www.inflearn.com