JPA 기본편

풀떼기·2023년 11월 21일

SpringBootLearn

목록 보기
7/8

실전편 강의를 듣다보니 제대로 익히지 않은 용어들이 많이 나와 이해하기 힘들었다. 야생형으로 실전1편 → 기초 → 실전2편을 들어가려고했는데 영속성 컨텍스트, 트랜젝션의 타이밍, 그 외에도 그냥 넘기기 힘든 말들이 많아 기본편으로 오게 되었다.

실무에서 어려운 이유

  • 객체와 테이블을 설계하고 정확하게 맵핑하는 것이 어렵다
  • 내부 동작 방식의 이해가 없다면 디버깅이 오래 걸린다

하지만 제대로 이해한다면 직접 sql문을 작성할 필요 없이 활용이 가능하다!

기본편은 총 16시간으로 되어있다. 이 아래로는 날짜별로 학습한 내용을 정리하려 한다.


2023.11.21

객체지향과 관계형DB

객체를 RDB에 저장하기 위해서는 sql문의 작성이 필수로 들어간다.
결국 개발자는 거의 sql 매퍼의 일을 하게 되는 것이다.

연관관계 : 객체는 참조를 사용하고, 테이블은 외래 키를 사용
→ 객체다운 모델링을 하기 어렵다
→ 객체답게 모델링 할수록 매핑 작업이 늘어난다

많은 사람의 고민 끝에 나온 게 바로 JPA(Java Persistence API)

  • 자바 진영의 ORM(Object-relational mapping) 표준 기술
  • 애플리케이션과 JDBC 사이에서 동작
  • JPA는 인터페이스의 모음이며, 구현체는 주로 Hibernate를 사용

성능 최적화

  • 1차 캐시와 동일성 보장
    - 같은 트랜잭션 안에서는 같은 엔티티를 반환
  • 트랜잭션을 지원하는 쓰기 지연
    - 트랜잭션을 커밋할 때까지 INSERT SQL을 모음
    - 한번에 SQL 전송
  • 지연 로딩과 즉시 로딩
    - 지연 로딩 : 객체가 실제 사용될 때 로딩

2023.11.22

JPA 맛보기

스프링이 아닌 순수 JPA만을 사용해 예제를 진행한다.
오랜만에 Maven을 써봤는데, 역시 Gadle이 간편하긴 한 것 같다.
강의 예제와는 다르게 최신 버전을 사용했다.
(hibernate-entitymanager 5.3.10.Fianl → hibernate-core 6.3.1.Final)

아래는 예제 코드로, 엔티티매니저팩토리를 불러온 후 엔티티매니저를 스레드에서 동작시킨다. 그리고 꼭 필요한 게 트랜잭션 연결인데, 트랜잭션을 열어두어야만 DB에 실제로 반영이 되기 때문에 commit까지 잊지 않고 작성해야한다.
에러가 발생할 경우를 대비해 try문으로 예외 발생시 롤백하는 코드를 추가시켰다.

    public static void main(String[] args) {
        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("hello");
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        EntityTransaction entityTransaction = entityManager.getTransaction();
        entityTransaction.begin();

        try {
            Member member = new Member();

            member.setId(1L);
            member.setName("HelloA");

            entityManager.persist(member);

            entityTransaction.commit();
        } catch (Exception e) {
            entityTransaction.rollback();
        } finally {
            entityManager.close();
        }

        entityManagerFactory.close();
    }

영속성 컨텍스트

영속성 컨텍스트를 명확하게 이해하면 JPA가 내부적으로 어떻게 돌아가는지 이해할 수 있다!

  • 엔티티를 영구 저장하는 환경이라는 뜻
  • 엔티티매니저를 통해서 영속성 컨텍스트에 연결
  • EntityManager.persist(entity) : entity를 영속화 한다. 영속성 컨텍스트에 저장시킨다.

엔티티의 생명주기

  • 비영속 : 객체를 생성만 한 상태(new)
  • 영속 : 영속성 컨텍스트에 객체를 저장한 상태(persist)
  • 준영속 : 영속성 컨텍스트에서 분리한 상태(detach)
  • 삭제 : 데이터베이스에서 삭제하겠다고 선언한 상태(remove)

플러시(flush)

영속성 컨텍스트의 변경 사항을 데이터베이스에 반영하는 것(동기화)

  • 변경 감지가 일어남
  • 수정된 엔티티를 쓰기지연 SQL 저장소에 등록
  • 쓰기지연 SQL 저장소의 쿼리를 데이터베이스에 전송
  • 트랜잭션이라는 작업 단위가 중요! 커밋 직전에만 동기화하면 됨

엔티티 매핑

@Entity

  • @Entity 어노테이션이 붙은 클래스를 JPA가 관리
  • 기본 생성자 필수!
  • final, enum, interface, inner 클래스 사용X
  • 저장할 필드에 final 사용X

2023.11.30

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

  • DDL을 애플리케이션 실행 시점에 자동 생성
  • 데이터베이스 방언을 활용해서 데이터베이스에 맞는 적절한 DDL 생성
  • 생성된 DDL은 개발 장비에서만 사용(운영에서 사용X)

주의할 것

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

❗ 될 수 있으면 사용하지 않는 것을 권장

DDL 생성 기능

  • 제약 조건 추가 @Column(nullable=false, unique=true)
  • DDL을 자동 생성할 때만 사용되고 JPA의 실행 로직에는 영향을 주지 않음

필드와 컬럼 매핑

어노테이션설명
@Column컬럼 매핑
@Temporal날짜 타입 매핑
@Enumeratedenum 타입 매핑
@LobBLOB,CLOB 매핑
@Transient특정 필드를 테이블에 매핑하지 않음

속성설명기본값
name필드와 매핑할 테이블의 컬럼 이름객체의 필드 이름
insertable, updateable등록, 변경 가능 여부true
nullablenull 값의 허용 여부를 설정
unique@Table의 uniqueConstraints와 같지만 한 컬럼에 간단히 유니크 제약조건을 걸 때 사용
columnDefinition데이터베이스 컬럼 정보를 직접 전달 ex) varchar(100) defualt'EMPTY'필드의 자바 타입과 방언 정보를 사용
length문자 길이 제약조건255
percision, scaleBigDecimal 타입에서 사용

Enum 타입을 매핑할 때

  • 옵션이 두 가지가 존재한다(ORDINAL, STRING)
    ❗ 운영에서 절대로 ORDINAL 타입을 사용하지 말것

기본키 매핑

  • 직접 할당 : @Id
  • 자동 생성 : @GeneratedValue
    • IDENTITY : 데이터베이스에 위임, MYSQL
      • 기본 키 생성을 데이터베이스에 위임
      • DB에 insert 쿼리를 실행한 뒤에야 Id값을 알 수 있다.
      • 영속성 컨텍스트에서 Id값을 알 수 없기때문에 em.persist() 시점에 insert쿼리가 날아간다.
    • SEAUENCE : 데이터베이스 시퀀스 오브젝트 사용, ORACLE(@SequenceGenerator 필요)
      • allocationSize로 성능 개선을 할 수 있다.(기본값 50)
      • 시퀀스 값을 미리 올려두는 방식으로 성능 개선
    • TABLE : 키 생성용 테이블 사용, 모든 DB에서 사용(@TableGenerator 필요)
      • 키 생성 전용 테이블을 하나 만들어서 데이터베이스 시퀀스를 흉내내는 전략
      • 장점: 모든 데이터베이스에 적용 가능
      • 단점: 성능
      • 운영에서 잘 사용되지는 않는다
    • AUTO : 방언에 따라 자동 지정

권장하는 식별자 전략

  • 기본 키 제약 조건 : not null, unique, 변하면 안된다.
  • 미래까지 이 조건을 만족하는 자연키는 찾기 어렵다. 대체키(랜덤, 시퀀스 등)를 사용하자.
  • 예를 들어 주민등록번호도 기본키로 적절하지 않다.
  • 권장 : Long형 + 대체키 + 키 생성 전략 사용
  • 비지니스를 키로 끌고오는 것은 권장하지 않는다.

연관관계 매핑

객체지향 설계의 목표는 자율적인 객체들의 협력 공동체를 만드는 것이다.

  • 연관관계의 주인과 mappedBy
    • 객체와 테이블간에 연관관계를 맺는 차이를 이해해야한다.
    • 객체의 양방향 관계는 서로 다른 단방향 관계 2개다.
  • 연관관계의 주인
    • 연관관계의 주인만이 외래 키를 관리(등록, 수정)
    • 주인이 아닌쪽은 읽기만 가능
    • 주인은 mappedBy 속성 사용 X
    • 외래 키가 있는 곳을 주인으로 정한다.
  • 순수 객체 상태를 고려해서 항상 양쪽에 값을 설정하자
  • 양방향 매핑 시에 무한 루프를 조심하자
    • 예) toString(), lombok, JSON 생성 라이브러리
    • 컨트롤러에서 엔티티를 반환하지 말자(dto로 반환)

정리

  • 단방향 매핑만으로도 이미 연관관계 매핑은 완료
    • 처음에는 단방향으로 끝낸다!
  • 양방향 매핑은 반대 방향으로 조회 기능이 추가된 것 뿐
  • JPQL에서 역방향으로 탐색할 일이 많음
  • 단방향 매핑을 잘 하고 양방향은 필요할 때 추가(테이블에 영향 X)

2023.12.01

연관관계 매핑

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

다대일 [N:1]

  • 가장 자주 쓰이는 매핑

일대다 [1:N]

  • 엔티티가 관리하는 외래 키가 다른 테이블에 있음
  • 연관관계 관리를 위해 추가로 update SQL 실행
  • 일대다 양방향도 사용할 수는 있지만 권장하지 않음

일대일 [1:1]

  • 주 테이블이나 대상 테이블 중에 외래 키 선택 가능
  • 어떤 테이블에 외래 키를 넣을지에 대해 많은 고민이 필요

다대다 [N:N]

  • 관계형 데이터베이스는 정규화된 테이블 2개로 다대다 관계를 표현할 수 없음
  • 한계가 많다
  • 연결 테이블을 추가해서 일대다, 다대일 관계로 풀어내야함
  • 연결 테이블을 엔티티로 승격!

상속관계 매핑

  • 관계형 데이터베이스는 상속 관계X
  • 슈퍼타입 서브타입 관계라는 모델링 기법이 객체 상속과 유사
  • 논리 모델링 기법을 세가지 방식으로 지원한다
    • JOINED : 조인 전략(기본적으로 정석적이다)
      • 장점 : 가장 정규화된 방식, 저장공간 효율화, 외래키 참조 무결성 제약조건 가능
      • 단점 : 조회 시 조인을 많이 사용, 성능 저하, 조회 쿼리가 복잡함
    • SINGLE TABLE : 단일 테이블 전략
      • 장점 : 일반적으로 조회 성능이 빠름, 조회 쿼리가 단순
      • 단점 : 자식 엔티티가 매핑한 컬럼은 모두 null 허용, 테이블이 커지는 상황에 따라 성능이 느려질 수 있다
    • TABLE_PER_CLASS : 구현 클래스마다 테이블 전략
      • 권장하지 않는 전략
      • 장점 : 서브타입을 명확하게 구분, not null 제약조건 사용 가능
      • 단점 : 여러 자식 테이블을 함께 조회할 때 느린 성능(UNION 쿼리 사용), 자식 테이블을 통합해서 쿼리하기 어려움
  • @DiscriminatorColumn : Dtype을 추가해준다. 부모 클래스에서 사용.
profile
주니어 백엔드 개발자입니다.

0개의 댓글