Web Spring 강의 정리[김영한/자바 ORM 표준 JPA프로그래밍 -기본편(1)]

ᄋᄌᄒ·2023년 4월 16일
0

Web_spring_inflearn

목록 보기
1/2
post-thumbnail

JPA 소개

📌 SQL 중심적인 개발의 문제점

객체와 테이블의 차이가 존재한다. 예를들면 객체는 상속 개념이 존재하지만 DB에서는 엄밀히 상속이라는 개념이 존재하지 않는다. 슈퍼타입 서브타입 관계를 통해 비슷한 기능을 해주는 것 뿐. 따라서 DB와 객체를 연결해주리란 쉽지 않은 것이다.

또한 객체다운 모델링과 DB의 모델링의 방식이 다르다 보니 매핑 과정이 까다로워질 수 있다. team과 member가 존재할 때 일반적으로 우리가 생각하는 모델은 객체와 가깝다. member는 team안에 소속되어있고, member를 통해 team 정보를 가져올 수 있지만, DB의 경우는 그저 테이블을 join하는 방식으로 관계를 가질 수밖에 없다.

JPA는 이를 해결해주기위해 존재하는 것이다.
즉, 객체와 DB사이의 간극을 메꿔 연결시켜주는 것이다.

📌 JPA 소개

JPA( java persistence API)
자바 진영의 ORM 기술 표준

ORM이란?
object-relational mapping
객체는 객체대로 설계하고 RDB는 RDB대로 설계하면 ORM프레임워크각 중간에서 매핑하는 역할을 해주어 객체와 RDB가 가지는 패러다임을 해결해준다.

JPA function

1. 1차 캐시와 동일성 보장
2. 트랜잭션을 지원하는 쓰기
3. 지연로딩과 즉시로딩

차레대로 설명하자면 1번 설명은 같은 트랜잭션 안에서는 같은 엔티티를 반환하도록 한다는 것이다. 1차 캐시는 DB로 정보를 보내기전에 사용하는 '바구니'같은 것이라고 봐도 될거 같다. 그 안에 저장할 entity 정보들을 담아 저장하고 수정하는 것이다.

그리고 이 정보들을 모은다. 트랜잭션을 커밋할때까지 insert SQL을 모으는 것이다. JDBC Batch SQL 기능을 사용해서 한번에 SQL을 전송하게된다.

지연로딩과 즉시로딩은 위에서 얘기한 1차 캐시와 관련되어있다. 1차 캐시는 DB와 객체를 연결해주는데, 지연로딩은 객체가 실제로 사용될 때 로딩되도록 설정되어있고, 즉시로딩은 SQL로 한번에 연관된 객체까지 미리 조회하는 방식이다.


JPA 시작하기

📌 영속성 컨텍스트

엔티티를 영구 저장하는 환경을 얘기하는 것이다.
생명주기는 아래와 같다.

1. 비영속 : 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태
2. 영속 : 컨텍스트에 관리되는 상태
3. 준영속 : 영속성 컨텍스트에 저장되었다가 분리된 상태
4. 삭제

entity manger를 통해 em.persist(member)와 같이 작성하여 영속상태가 된다고 해서 바로 DB에 쿼리가 날아가는 것이 아니다. 쿼리를 날리려면 트랜잭션 커밋이 되어야 한다.

영속성 컨텍스트의 유효성은 트랜잭션과 같다. 트랜잭션을 마치면 영속성 컨텍스트는 제거된다.

변경 감지의 경우 변경 전 entity의 스냅샷, 즉 복사본을 만들어두고 변경 후에 변경된 entity와 스냅샷을 비교한다. 만약 변경 부분이 존재한다면 update 쿼리를 만들어서 flush하는 것이다. 참고로 flush는 트랜잭션의 끝이 아니다. 그저 데이터베이스에 1차 캐시에서의 변경점 동기화 시켜주는 것 뿐이다.

이외에도 다음과 같은 상태들이 존재한다.

1. em.detach: 특정 엔티티만 준영속 상태로 전환
2. em.clear: 영속성 컨텍스트를 완전히 초기화
3. em.close: 영속성 컨텍스트를 종료

Entity Mapping

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

1. create : drop + create
2. create-drop : drop + create + drop
3. update : 변경분만 반영
4. validate : 엔티티와 테이블이 정상 매핑되었는지 확인
  • 권장 사용법
    개발 초기 : create or update
    테스트 서버 : update or validate
    스테이징과 운영 서버 : validate or none

(운영 장비에서는 create,update는 함부로 사용하지말자.)

📌 필드와 컬럼 매핑

@Enumerated(Enum.Type.String) :
Ordinal 방식으로 사용하면 enum 타입을 더 추가했을 시 이전에 저장했던 데이터베이스 안의 정보들은 변경되지 않기 때문에 오류가 발생할 수 있다. 따라서 순서가 아닌 String으로 저장하도록 한다.

📌 기본 키 매핑

@GeneratedValue

1. identity
2. table
3. sequence
4. auto

identity는 DB가 알아서 값을 부여하는 것이다. 원래 영속성 컨택스트로 인해 트랜잭션이 커밋됐을 때 DB에 쿼리가 날라가지만, ID 자동생성 전략을 사용하면 영속성컨텍스트로 담자마자 ID 생성과 함께 insert 쿼리가 DB에 날라간다.

table은 키 생성 전용 테이블을 하나 만들어 데이터베이스 시퀀스를 흉내낸다. 장점으로는 모든 데이터베이스에 적용가능하다.

sequence는 아이디 테이블이 따로 존재한다. 따라서 ID값만 DB에서 가져오면 되기 때문에 일반적인 영속성 컨텍스트처럼 똑같이 작동한다. 이런 방식은 성능측에 우려가 되지 않을 까 생각이 들 수 있다. 네트워크를 여러번 왔다갔다 해야하기 때문이다. 그렇다면 identity처럼 insert 쿼리를 날리는 것이 효과적으로 보일 수도 있다. 이는 allocateSize를 통해 이것을 해결한다.
allocateSize는 설정 크기만큼 메모리에서 사용하고 나서 쿼리로 날려줄 수 있다.


연관관계 매핑 기초

📌 단방향 연관관계 & 양방향 연관관계

데이터베이스는 연관관계를 외래키 하나로 join하여 사용할 수 있지만 entity는 참조용 필드를 추가해야 된다. 이때 두 entity가 연관관계를 맺는다고 했을 때 둘다 참조용 필드를 추가해야할까?
참조는 비즈니스 로직에 따라 설정하면된다. 한쪽 entity에서만 정보를 가져와도 충분하다면 단방향으로, 두 entity의 정보를 가져와서 서로의 정보를 가져와야한다면 양방향으로 연관관계를 설정해주는 것이다.

일대다, 다대일 관계이서는 서로의 관계를 이어주고 매핑할 수 있도록 설정해주어야 한다.'다' 객체에서 '일'객체를 매핑할 수 있도록 하는 방법은@ManyToOne@Join(name="") 을 통해 자신과 연결된 객체를 테이블의 어떤 값을 통해 매핑할 것인지를 설명하면 됐다. 반대로 '일'객체에서 '다'객체를 매핑할 수 있도록 하는 방법은 @OneToMany@JoinColumn(mappedBy="") 인데, 여기서 mappedBy안에 들어갈 값은 반대 객체에서 자신을 매핑하는 객체의 변수 이름을 적어야한다. 이렇게 설정하면 두 객체는 서로 양방향 연관관계를 맺어 서로가 서로를 매핑할 수 있게끔 설계된 것이다.

객체에서 양방향 연관관계는 사실 단방향 연관관계 두개를 얘기하는 것이다. 그렇다면 중요한 것은 두 연관관계의 주인이 누구냐는 것이다.
두 객체는 서로 관계를 맺고 있는 상태인데, 만약 FK의 업데이트가 이루어진다면 둘다 수정을 해야하는 것인지, 아니면 둘 중 하나만 수정을 하면 되는 것인지 헷갈리기 시작한다.
테이블은 단순히 FK만 수정하면 된다.
그렇다면 두 객체 중 하나를 선택하여 FK를 관리하도록 세팅하면 된다. 한 객체가 FK가 수정된다면 자동으로 반대 객체에는 그에 맞는 연관관계를 수정하는 것이다.
이 관점으로 봤을 때 두 객체의 주인을 정한다고 보는 것이다. 주인은 곧 FK를 관리하는 객체이다. 그리고 mappedBy를 통해 매핑하는 객체는 주인이 될 수 없다. 왜냐하면 "누군가를 통해 매핑이 된다."라는 해석으로만 봤을 때도 주체적이지 못하기 때문이다. 이러한 성질 때문에 주인이 아닌 객체는 조회만 가능하도록 설정된다.

  • 주의점
    양방향 매핑 시 주인에만 값을 세팅한다. 하지만 역방향 값을 세팅 안해줬을 때 문제가 생길 수 있다. 1차 캐시에 값을 세팅해줬을 때는 아직 쿼리 생성이 되지 않았기 때문에 FK세팅이 안된 상태이다. 원래라면 DB에서 FK를 보고 list가져오는 것이지만 이 경우에는 FK 설정이 안된 것이기 때문에 조회가 되지 않는다.이 문제를 해결할 수 있는 방법은 연관관계 메서드를 생성하는 것이다.

결론 : 단방향 매핑을 잘하고 양방향은 필요할 때 추가해도 됨.

0개의 댓글