JPA (Java Persistence API)

박영준·2023년 1월 3일
0

JPA

목록 보기
3/8

1. 정의

  • 애플리케이션을 객체지향 언어로 개발하고 & 관계형 데이터베이스로 관리한다면
    객체-관계형간의 차이를 해결하기 위해 JPA 를 사용

  • 자바 진영에서 ORM(Object-Relational Mapping)기술에 대한 표준으로 사용되는 인터페이스의 모음
    → 즉, 실제로 구현된 것이 아니라, 구현된 클래스와 mapping해주기 위해 사용되는 프레임워크 O

    참고: Spring (스프링 - 2) 프레임워크 vs 라이브러리

  • 자바 ORM 기술에 대한 표준 명세

    참고: ORM (Object Relational Mapping, 객체-관계 매핑)

  • 영속성 관리 도구

    영속성: 데이터를 생성한 프로그램의 실행이 종료되더라도 사라지지 않는 데이터의 특성

    영속성 컨텍스트
    - 엔티티를 영구 저장하는 환경
    - 애플리케이션과 데이터베이스 사이에서 객체를 보관하는 가상의 데이터베이스 같은 역할
    참고: 영속성 컨텍스트

  • 쿼리 자동 생성

    쿼리(Query): 데이터베이스에 정보를 요청하는 것

  • 최적화

  • sql을 지원한다면, jpa가 방언들도 알아서 처리

    방언 (Dialect)
    SQL은 ANSI SQL(표준 SQL)이 있으며,
    이 외에도 각 DBMS Vendor(벤더, 공급업체)인 MS-SQL, Oracle, MySQL, PostgreSQL 에서 자신만의 기능을 추가한 SQL이 있다.
    ANSI SQL이 모든 DBMS에서 공통으로 사용 가능한 핵심 표준 SQL(= 대한민국의 수도인 서울에서 사용하는 표준어)이지만,
    여러 제품의 DBMS에서는 자신만의 독자적인 기능을 위해서 추가적인 SQL(= 여러 지방에서 사용하는 방언)을 만들었다.
    (예를 들면, MS-SQL의 T-SQL, Oracle의 PL/SQL)

  1. DB vs JPA
  • DB : 데이터를 어딘가에 적어둔 다음 두고두고 이용하고, 관리
  • JPA
    - Java를 이용해서 DB와 편하게 소통 (단기 작업용이라 휘발되어도 상관없는 데이터들)
    - 작업이 빠르고 견고하게 일어날 수 있도록 도움

    어플리케이션이 데이터베이스를 직접 다룰 때의 문제점

    1. 훨씬 더 번거로움
    2. SQL 의존적이라 변경에 취약
    3. 객체지향 모델과 관계형 데이터베이스의 패러다임 불일치
      --> 이 문제의 해결책으로 ORM(객체 관계 매핑), JPA이 등장

2. 필요성

1) 관계형 데이터베이스(RDB)

웹 어플리케이션에는 객체를 관계형 데이터베이스에서 관리하는 것이 중요하고, 관계형 데이터베이스가 웹 서비스의 중심이 되었다.
그러다보니, SQL이 어플리케이션 코드 보다 많아졌고, 이를 해결하기 위해 JPA 가 등장했다.

참고: SQL (Structured Query Language, 구조적 질의 언어)

2) 많은 테이블과 SQL

현업에서는 수십~수백 개의 테이블 & 그보다 더 많은 SQL 을 직접 만들고, 유지보수를 해야한다.

3) 객체와 DB의 패러다임 불일치 문제

객체지향 프로그래밍 언어 : 메시지 기반. 기능과 속성을 한 곳에서 관리
관계형 데이터베이스 : 어떻게 데이터를 저장할지 → DB 는 데이터 중심으로 구조화되어있으므로(객체의 상속, 다형성 같은 개념이 없음)

→ 즉, 관계형 데이터베이스에서는 객체지향 프로그래밍 언어를 표현할 수 X

예시 (1)

Java 에서는 객체의 상속 관계 지원 O
RDB 에서는 객체의 상속 관계 지원 X

예시 (2)

객체지향 프로그래밍에서, 부모 객체를 가져오고 싶은 경우

// user와 group은 부모-자식 관계이다.
User user = findUser();
Group group = user.getGroup();

++ 여기에 DB 가 추가된 경우

User user = userDat.findUser();
Group group = groupDat.findGroup(user.getGroupId());

user와 group을 각각 따로 조회하게 된다.
→ user와 group이 서로 어떤 관계인지 정확히 알 수 X
→ 이 때문에, 객체 모델링을 DB에서는 구현할 수 없게 돼버림
→ 이를 해결해주는것이 JPA

3. 장단점

1) 장점

  • SQL 문이 아닌 Method 를 통해 DB 를 조작하므로, 비즈니스 로직 구성에만 집중 가능

  • 특정 DB 에 종속되지 X
    DB 를 바꿔야 할 경우, 각 EB 마다 쿼리문이 다르므로 전체적인 수정을 해야하지만
    JPA 를 사용할 경우, 설정 파일만 바꿔서 DB 를 얼마든지 변경 가능

  • DB 테이블에 새로운 컬럼이 추가된 경우,
    JPA를 사용 하지 않으면, DTO 클래스의 필드를 모두 변경해야하지만
    JPA를 사용하면, 테이블과 mapping 된 Class에 필드만 추가하면 된다.

  • SQL 문을 직접 작성하지 X
    → 유지보수, 재사용성 좋음

2) 단점

  • JPA 학습에 시간 多

  • 프로젝트 규모가 크거나 or 복잡 or 설계 오류 일 경우, 속도↓ + 일관성 X

  • 복잡한 쿼리 사용 할 경우(통계 같은), 속도를 위해 별도의 튜닝일 필요해져서 SQL 을 사용해야할 경우가 발생

4. 종류

public enum CascadeType{

    ALL, // 모두적용
    PERSIST, // 영속화
    MERGE, // 병합
    REMOVE, // 삭제
    REFRESH, // 새로고침
    DETACH // DETACH
}

5. 사용법

1) 객체 저장

jpa.persist(member);

2) 객체 조회

Member member = jpa.find(memberId);

3) Spring Data JPA

(1) 정의

Spring Data JPA

  • JPA를 wrapping 해서, 사용하기 편하게 만들어주는 라이브러리

EntityManagerFactory

  • Persistence 를 통해 생성가능

    Persistence.createEntityManagerFactory("unit명");
  • EntityManager 를 생성하는 Factory

  • MyBatis 에서의 SqlSessionFactory 에 해당

  • 어플리케이션 전체에서 하나만 생성해서 공유해야 함

  • 최근의 프로그램들은 성능을 위해 여러 Thread들이 일을 같이하게 되어 있음
    → 하나의 큰 일을 동시에 처리하려다 보면, '동시성 문제' 발생!

    해결법?
    이를 방지하기 위해, 특정 리소스나 정보는 공유하지 못하게 하는 등... 처리가 필요.
    → EntityManager 에서 공유하면 안되는 특정 리소스나 정보를 여러 Thread가 하나의 EntityManager를 이용 할 수 없도록 처리!
    → 그래서, EntityManagerFactory 에서 필요 할 때마다, 여러개의 EntityManager를 생성하는 구조로 설계한 것
    (하나의 Thread가 하나의 EntityManager를 이용하도록!)

    EntityManagerFactory가 있기 때문에,
    EntityManager를 여러개 생성할 필요 X
    매번 모든과정을 다시하지 X
    일의 비용 ↓

    참고: 운영체제 (OS) - 스레드 (Thread)

EntityManager

  • 영속성 컨텍스트 내에서 Entity들을 관리

  • JPA에서 제공하는 interface로 spring bean으로 등록되어 있어, Autowired로 사용 가능

    @Autowired
    private EntityManager entityManager;
  • Query Method, Simple JPA repository는 직접적으로 entityManager를 사용하지 않도록 한번 더 감싸준 것

  • spring jpa 에서 제공하지 않는 기능을 사용 or 특별한 문제가 있어서 별도로 customizing을 해야한다면, entityManager를 직접 받아서 처리

  • Entity Cache를 갖고 있음

  • entityManager 와 em 은 코드에서 동일한 의미

(2) 사용법

Spring Data JPA 를 사용했을 경우

// Entity를 생성!
Member minsook = new Member();
member.setId("abcd1234"); 
member.setUsername("민숙");

//아래의 내용도 똑같은 과정
memberRepository.save(minsook);
memberRepository.find();

Spring Data JPA 를 사용하지 않을 경우
1. Entity를 생성!

Member minsook = new Member();
member.setId("abcd1234"); 
member.setUsername("민숙");
  1. EntityManagerFactory 만들기
//EntityManager를 생성해줄 EntityManagerFactory를 만들어야합니다.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpa심화주차");
  1. EntityManagerFactory에서 EntityManager 생성
// Entity를 관리해줄 EntityManager를 EntityManagerFactory에서 생성!
EntityManager em = emf.createEntityManager();
  1. EntityManager에서 Entity 저장 및 찾기
// 엔티티를 영속화(저장)
em.persist(minsook);
// 엔티티를 찾기
em.find(Member.class, 100L);

참고: JPA, Spring Data JPA, Hibernate 비교

4) 영속성 전이 (Cascade)

(1) 정의

  • Cascade = "연쇄"

  • 특정 엔티티를 영속 상태로 만들 때, 연관된 엔티티도 함께 영속 상태로 만들고 싶은 경우에 사용
    → 즉, 영속성 전이를 사용하면, 부모 엔티티를 저장할 때 자식 엔티티도 함께 저장

  • JPA 는 CASCADE 옵션으로 영속성 전이를 제공

  • 영속성 전이는 연관관계를 매핑하는 것과 아무 관련 X
    단지, 엔티티를 영속화 할 때 연관된 엔티티도 같이 영속화하는 편리함을 제공할 뿐이다.

(2) 사용법

① @OneToMany 가 걸린 엔티티에서 사용

//영속성 전이 설정
@OneToMany(mappedBy = "person", cascade = CascadeType.ALL)
private List<Address> addresses;

② 영속성 전이: 저장

  1. @Setter
    @Getter
    @Entity
    public class Parent {
    
        @Id @GeneratedValue
        @Column(name = "PARENT_ID")
        private Long id;
    
        private String name;
    
        // cascade = CascadeType.PERSIST : 부모를 영속화 할 때, 자식들도 같이 영속화
        @OneToMany(mappedBy = "parent", cascade = CascadeType.PERSIST)
        private List<Child> children = new ArrayList<Child>();
    }
  2. // cascade = CascadeType.PERSIST 덕분에, 부모와 자식 엔티티를 한 번에 영속화 할 수 있게 됨
    @Test
    @Transactional
    @Rollback(false)
    public void printUser() throws Exception {
    
        // 1번 자식 저장
        Child child1 = new Child();
        // 2번 자식 저장
        Child child2 = new Child();
    
        Parent parent = new Parent();
        parent.setName("임종수");
    
        child1.setName("임준영");
        child2.setName("임주리");
    
        child1.setParent(parent); // 자식 -> 부모 연관관계 설정
        child2.setParent(parent); // 자식 -> 부모 연관관계 설정
    
        parent.getChildren().add(child1); // 부모 -> 자식
        parent.getChildren().add(child2); // 부모 -> 자식
    
        // 부모 저장
        entityManager.persist(parent);
    }
  3. CASCADE 실행

쿼리 결과를 보면 데이터가 정상적으로 2건 입력된 것을 확인할 수 있다.

③ 영속성 전이: 삭제

부모와 자식 엔티티를 모두 일일이 제거하려면

Parent findParent = em.find(Parent.class, 1L);
Child findChild1 = em.find(Child.class, 1L);
Child findChild2 = em.find(Child.class, 2L);

em.remove(findChild1);
em.remove(findChild2);
em.remove(findParent);

영속성 전이를 이용해서 제거하려면
1.

@Setter
@Getter
@Entity
public class Parent {

    @Id @GeneratedValue
    @Column(name = "PARENT_ID")
    private Long id;

    private String name;

	// cascade = CascadeType.REMOVE : 부모를 삭제 할 때, 자식들도 같이 삭제
    @OneToMany(mappedBy = "parent", cascade = CascadeType.REMOVE)
    private List<Child> children = new ArrayList<Child>();
}
// 부모 엔티티만 삭제하면 연관된 자식 엔티티도 함께 삭제 됩니다.
Parent findParent = em.find(Parent.class, 1L);
em.remove(findParent);
  • 삭제 순서 : 외래키 제약조건을 고려해서, 자식을 먼저 삭제하고 부모를 삭제
  • CascadeType.REMOVE 를 설정하지 않고 이 코드를 실행하면 부모 엔티티만 삭제된다.
    하지만 데이터베이스의 부모 로우를 삭제하는 순간,
    자식 테이블에 걸려 있는 외래 키 제약조건으로 인해, 데이터베이스에서 외래 키 무결성 예외가 발생

④ 회원 탈퇴의 로직 구현

⑤ Cascade 를 사용할 상황

  1. Post게시글, Image사진
    Post게시글에 첨부되는 Image사진들은 Post게시글이 생성될때 함께 생성되고 삭제될때 함께 삭제된다.

    // Image 엔티티
    @OneToMany(cascade = CascadeType.ALL)
    private List<Image> iamge;

    따라서, 이렇게 해도 아무런 문제가 없다.

  2. Post게시글, Comment댓글
    Comment댓글이 달린 Post게시글이 삭제될 경우, 유저는 본인이 쓴 Comment댓글을 더이상 확인할 수 없게 된다.
    (유저 본인이 쓴 댓글이 필요가 없으면 Cascade를 걸어도 되겠지만, 그렇지 않은 경우라면 Cascade를 걸면 안될 것이다.)

⑥ 여러 속성 같이 사용

cascade = {CascadeType.PERSIST, CascadeType.REMOVE}

em.persist(), em.remove()를 실행 할 때 바로 전이가 발생하지 않고, flush 를 호출 할 때 전이가 발생

⑦ CASCADE 의 생명주기

예시 1

@OneToMany(mappedBy="parent", cascade=CascadeType.ALL, orphanRemoval=true)
private List<Child> children = new ArrayList<>();
  • JPA 에서 em.persist(), em.remove() 를 사용하여 엔티티의 생명주기를 관리
  • 엔티티는 CASCADE ALL 과 orphanRemoval 을 true 로 주어서 부모 엔티티에서 자식 엔티티의 생명주기를 관리하도록 할 수 있다.

예시 2

//자식을 저장하려면 부모에 등록만 하면 됩니다.
Parent parent = em.find(Parent.class , parentId);
parent.addChild(child);

//자식을 삭제하려면 부모에서 제거하면 됩니다.
Parent parent = em.find(Parent.class , parentId);
parent.getChildren().remove(removeObject);

6) 고아 객체

(1) 정의

부모 엔티티와 연관관계가 끊어진 자식 엔티티

(2) 사용법

@OneToMany(mappedBy="parent", orphanRemoval=true)
private List<Child> children = new ArrayList<>();

부모 엔티티에서 orphanRemoval 옵션을 true 로 주어서, 자동 삭제하도록 한다.
(단, @OneToOne, @OneToMany 만 사용가능)

주의! 2가지 조건을 모두 만족할 경우에만 사용
조건 1 : 해당 엔티티를 참조하는 곳이 하나
조건 2 : 특정 엔티티가 개인 소유하는 경우

그렇지 않은 경우에 사용하면, 해당 엔티티를 참조하는 다른 곳에서 조회가 실패되어 문제가 발생

@Setter
@Getter
@Entity
public class Parent {

    @Id @GeneratedValue
    @Column(name = "PARENT_ID")
    private Long id;

    private String name;

    @OneToMany(mappedBy = "parent", orphanRemoval = true)
    private List<Child> children = new ArrayList<Child>();
}
Parent parent1 = em.find(Parent.lcass, id);
parent1.getChildren().remove(0); // 자식 엔티티를 컬렉션에서 제거

// 모든 자식 엔티티를 제거하려면
// parent1.getChildren().clear();

cascade REMOVE 와 orphanRemoval = true 차이점
1. orhanRemoval 은 관계 여부에 따라 달라진다.
자식 엔티티가 삭제된게 아니더라도, 부모와 관계가 끊어지면 자식 엔티티는 지워진다.
orphanRemoval이 cascade remove보다 큰 개념.

  1. 고아객체 자동 삭제(orphanRemoval = true) 설정은 자식 엔티티가 해당 부모 엔티티랑만 관계를 맺을 때 사용해야한다.
    그렇지 않으면 다른 관계들도 삭제가 된다.

  2. 관계가 끊어져도 자식엔티티가 존재해야하는 상황에서는 사용 X

6. 주의점

  1. findBy 의 사용
    public interface MemberRepository extends JpaRepository<Member, Long>{
        List<Member> findByEmailAndName(String email, String name);
    }
    repository 에서 findBy 사용 시 자동 완성으로 코드를 완성하게 되는데
    이 때, 그 뒷 부분을 놓치지 않고 써줘야 메소드가 제대로 작동한다.
    → 즉, findByEmail (X) / findByEmailAndName (O)

JPA 를 활용하기 위해서는
객체와 관계형 데이터베이스를 매핑(Object Relational Mapping)영속성 컨텍스트
이 2 가지를 이해할 필요가 있다.

JPA 연관관계 (+ ERD)

참고: JPA 연관관계 (+ ERD)

영속성 컨텍스트

참고: 영속성 컨텍스트 (persistence context)


참고: JPA 다대일 단방향 양방향
참고: JPA 연관 관계 한방에 정리 (단방향/양방향, 연관 관계의 주인, 일대일, 다대일, 일대다, 다대다)
참고: 다대다
참고: JPA의 정의와 장·단점
참고: JPA란 무엇인가 (Before JPA, 영속성 컨텍스트, Entity, JPQL, 트랜잭션, N+1 문제)
참고: JPA 영속성 전이
참고: 알고 쓰는 Cascade(영속성 전이)
참고: [JPA] 영속성 전이 (CASCADE)
참고: JPA 영속성 전이
참고: 프레임워크(Framework)란? 개념, 장단점, 종류

profile
개발자로 거듭나기!

0개의 댓글