자바 ORM 표준 JPA 프로그래밍 - 기본편 / 기본 개념과 연관관계 매핑

60jong·2022년 10월 4일
0

JPA

목록 보기
1/5
post-thumbnail

신경 쓸 것!!

  • setter대신 (1) static 메서드로 객체 생성 후 주입 / (2) 생성자로 주입
  • setter대신 별도의 메서드를 만들어서 변경 (객체 수정 시에)
  • 영속성 객체 vs 준영속성 객체 ---- 객체를 직접 생성해서 em.merge()대신 em.find 등으로 영속성 객체를
    받아서 수정 --> JPA가 변경 감지 (dirty checking)를 통해 update 해줌.

JPA - 객체와 테이블을 매핑!!

why JPA??

  • SQL 중심 개발의 문제점
    현재 시대는 객체를 관계형 DB에 저장
    -> sql 중심의 개발이 된다.

  • 패러다임의 불일치
    객체 vs 관계형 DB

객체 지향 언어는 래퍼런스 저장할 수 있다.
하지만 DB에는 래퍼런스 저장이 불가능하다.
(예시, 자바 컬렉션에 객체를 저장하면
해당 객체 내부의 객체들 모두 조회 가능!
하지만 DB에서 조회한 객체는 직접 매핑을
해줘야만 가능하다.)

-> 자바 컬렉션처럼 db에 저장할 수는 없나?

JPA - Java Persistence API
자바 진영의 ORM 기술 표준

ORM - Object-Relational Mapping (객체 관계 매핑)

영속성 컨텍스트 (Persistence Context)

  • 논리적인 개념으로, em 내부에 생성된다. (엔티티를 저장하는 환경)
  • em.persist(member) -> 영컨 내부에 1차 캐시에 등록됨(바로 DB저장 x, 따라서 1차캐시에 존재하는 객체 조회 시에는 쿼리 안 날라감)
    -> em.find(member) 1차 캐시에서 반환 (1차 캐시에 없는 경우, DB에서 조회해서 1차 캐시에 저장 후 반환)
    - 1차 캐시에 등록됨과 동시에 sql문을 생성해서 쓰기 지연 SQL 저장소에 등록한다. (이는 트랜잭션 커밋시에 날림)
    버퍼 개념으로 batch라 부름 (batch_size로 조절 가능)
  • 엔티티 매니저는 한 트랜잭션 안에서만 살아있다.

영속, 비영속, 준영속

  • 영속 상태, 엔티티 매니저의 영속성 컨텍스트의 1차 캐시에 등록된 상태
  • 비영속 상태, 영컨과 관계 없는 일반 객체 상태
  • 준영속 상태, detach() 영컨에서 관리 x

객체와 테이블 매핑

@Entity가 붙으면 JPA가 관리하는 클래스 (public or protected 기본 생성자 필수)

데이터베이스 스키마 자동 생성

데이터베이스 방언을 활용해 DB에 맞는 적절한 DDL을 생성
hibernate.hbm2ddl.auto
.create, drop + create
.create-drop, drop + create + drop
.update, 변경분만 적용
.validate, 엔티티 - 테이블 정상 매핑됐는지만 확인
.none

주의!! 운영 장비에는 절대 create, create-drop, update 사용 X
개발 초기 단계는 create 또는 update
테스트 서버는 update 또는 validate
스테이징과 운영 서버는 validate 또는 none

필드와 컬럼 매핑

@Column
여러 옵션으로 설정 가능

기본키 매핑

@Id -> pk 지정
@GeneratedValue -> 자동 입력 / 전략 - identity, sequence, table
추천 :Long + 대체키 + 키 생성 전략

identity 전략은 id 생성을 DB에 위임 -> JPA는 null값으로 id값을 넘기기에 영컨에 저장할 id가 없다.
(하지만 영컨의 1차 캐시에서는 엔티티를 id로 구분했다 -> 커밋시점이 아니라 바로 sql 날려서 id값 받아온 뒤 1차캐시에 등록)

sequence 전략은 em.persist()시에 db에 저장돼있는 next_sequence받아와서 setId()한 뒤에 영컨에 저장
-> 매번 저장할 때마다 db에 접근하면 성능 이슈?? 그래서 allocationSize = 50
-> 메모리에서 next_seq를 저장해가다가 seq == 50 -> db에 접근해 next_seq (100)을 받아옴.

이 방식은 여러 서버가 켜있어도 문제가 없다. (본인의 max를 받아 놓기에)

엔티티에 객체가 아닌 객체의 id를 저장한다면, 객체 중심 < 테이블 중심의 설계가 된다. + 객체 조회하려면 계속
em.find필수

따라서 엔티티에 객체의 id가 아닌 객체를 저장해야한다!!

연관관계 매핑

객체 - 테이블 연관관계
객체 - 참조 ~~ 테이블 - 외래 키

  • 방향 단/양
  • 다중성
  • 연관관계의 주인

단방향 연관관계

단순히 테이블 간에 외래 키로만 참조가 단방향으로 이루어지는 경우에 @xTox 어노테이션 + @JoinColumn으로
매핑이 가능하다.

양방향 연관관계와 연관관계의 주인

양방향 연관관계, 두 테이블 간에 단순히 외래 키로만 참조가 단방향이 아닌 서로 참조가 이루어질 때!
(member, team 참조 | team, memberList 참조)

테이블에서는 외래 키로 양 테이블을 왔다갔다 가능!!
객체는 불가능하다. -> 양방향 연관관계

List members 에 @OneToMany(mappedBy = "team" ) -> 참조 연결이 될 객체 명 매핑

  • mappedBy
    객체 - 테이블이 관계를 맺는차이

객체 : 회원 -> 팀 & 팀 -> 회원 | 사실 서로 다른 단방향 관계가 2개
테이블 : 회원 <-> 팀 | 방향X, 외래키 하나로 두 테이블 연관관계 관리(join)

회원, 팀 모두 서로를 참조하고 있기에 테이블의 외래키 (team_id)를 관리를 누가할 것인가에 문제가 생긴다.
결론부터 얘기하자면, 외래 키를 가지고 있는 테이블의 엔티티가 관리한다. 그리고 그런 엔티티를
연관관계의 주인이라고 명명한다.
그 이유는 회원 (팀 참조), 팀 (많은 회원 참조) 상황에서

회원이 팀을 바꿨다.
회원이 주인-> 회원 엔티티, 팀 참조 변경 -> 회원 테이블, 팀 id 변경
팀이 주인 -> (일단 벌써 복잡하다...) 팀 엔티티, 회원 리스트에서 회원 삭제 + 회원 엔티티 팀 참조 변경 -> 팀 테이블도 같음

팀이 주인이 된다면 회원이 팀을 변경하는 상황에 너무 많은 변경 사항이 필요하다. 하지만
회원이 주인이라면 간단히 팀 id만 변경하면 되니 너무나도 직관적이다!!

그리고 반드시 연관관계 주인에는 값을 set해야한다!!!!

차라리 양방향 연관관계에서는 둘다 넣어버리는게 편하다.
-> 까먹지않게 연관관계 편의 메서드를 정의하자!! (member.setTeam() 안에다가 team.getMembers.add하도록!)
(em.flush()를 하고 1차 캐시에 업데이트를 하지 않는다면
1차 캐시에 없기에 조회가 안된다.) !!!항상 1차 캐시 신경쓰자!!

예제!!

고급 매핑
상속관계 매핑
관계형 DB에는 상속관계 x (유사한 것은 슈퍼타입 - 서브타입)
조인전략 / 단일 테이블 전략 / 구현 클래스마다의 테이블 전략

1은 정규화도 돼있고, 저장공간 효율

2은

3은 Item타입으로 조회할 때, 모든 테이블을 모두 검색해야해서 비효율!!
위 전략들을 바꿔도 코드 손댈 것이 없다... -> 개꿀 ㅎ

Mapped superclass

profile
울릉도에 별장 짓고 싶다

0개의 댓글