-
[jpa] 연관관계 매핑 기초
intro : jpa의 연관관계 매핑에 대해 알아보자.
연관관계가 필요한 이유
다음과 같은 상황에 대해서 생각해보자. 회원과 팀이 있고 회원은 하나의 팀에만 소속될 수 있다. 회원과 팀은 다대일 관계이다. 이 내용을 토대로 객체를 테이블에 맞추어 모델링한 결과는 다음과 같다.
참조 대신에 외래키를 그대로 사용하여 Entity를 구성
외래키 식별자를 직접 다룸
식별자로 다시 조회, 객체 지향적인 방법은 아님
위 방법이 왜 객체지향적인 방법이 아니라고 말하는걸까 ?
객체는 참조를 사용해서 연관된 객체를 찾는다.
구성된 클래스에서 teamId를 통해 Member 클래스와 Team 클래스의 연관관계를 지정하고 있다. 이는 사실상 데이터베이스의 연관관계 방식을 객체에 그대로 적용한 것으로, 객체지향적 관계와는 거리가 멀다고 할 수 있다. 데이터베이스 중심의 매핑은 객체의 본질적인 특징인 캡슐화와 추상화를 무시하고, 단순히 데이터 저장소의 구조를 따라가는 방식이다. JPA를 활용할 때는 객체지향적 연관관계 매핑을 통해 객체의 본질을 유지하고 데이터 처리의 복잡성을 줄이는 것이 중요하다. 객체의 연관성을 직접 매핑하고, 이를 통해 자연스러운 탐색과 유지보수 가능한 설계를 구현해야 한다. 이는 객체지향적인 코드와 데이터베이스 간의 간극을 효과적으로 줄여주며, 장기적으로 더 나은 성능과 가독성을 제공한다.
단방향 연관관계
위 상황과 다르게 이번엔 객체 중심으로 모델링한 결과는 다음과 같다.
객체의 참조와 테이블의 외래 키를 매핑 (위 상황과 다르게 객체 자체로 키 매핑)
연관관계 저장
참조로 연관관계 조회 - 객체 그래프 탐색
연관관계 수정
양방향 연관관계와 연관관계 주인
이번에는 객체가 단방향 연관관계가 아니라, 양방향 연관관계인 경우이다. 테이블은 외래키 하나로 양쪽 테이블의 데이터를 조회할 수 있지만, 단방향으로 설정된 객체에서는 Member는 Team을 알 수 있어도 Team에서는 Member를 알 수 없다.
Member 엔티티는 단방향 동일
Team 엔티티는 컬렉션 추가 (members), mappedBy 사용
반대 방향으로 객체 그래프 탐색
연관관계 주인과 mappedBy
객체와 테이블간에 연관관계를 맺는 차이를 이해해야 한다.
객체의 연관관계는 2개이다, 회원에서 팀으로의 연관관계 + 팀에서 회원으로의 연관관계 2개. 테이블 관점에서는 회원에서 팀의 pk값을 가지고 있기에 연관관계는 하나지만 양방향으로 조회할 수 있다. 정리하자면 테이블은 외래 키 하나로 두 테이블의 연관관계를 관리한다 그렇다면 객체에서 또한 어느 곳에서 하나로 외래키를 관리해야 한다. 이때 사용하는것이 mappedBy인데, 주인이 아닌곳에 속성을 사용하게 된다. 보통 주인을 결정할 때는 외래키가 있는 곳을 주인으로 정한다. 위 상황 에서는 Member.team이 연관관계의 주인이 된다.
양방향 매핑시 가장 많이 하는 실수
양방향 매핑시 연관관계의 주인에 값을 입력해야 한다.
순수한 객체 관계를 고려한다면 항상 양쪽 다 값을 입력해야 한다. (Member에 Team을 설정해 줘야 한다.)
양방향 연관관계 주의!
순수 객체 상태를 고려해서 항상 양쪽에 값을 설정하고, 실수할 수도 있으니 연관관계 편의 메소드를 생성해서 사용하자 (권장) set 보다 change 라는 단어가 포함된 이름의 메소드를 구성하는게 관례이며 보통 연관관계 메소드는 한쪽에만 작성한다. 추가적으로 양방향 매핑시에는 무한루프를 조심해야 하는데 toString(), lombok, JSON 생성 라이브러리 등 무한 루프에 빠질 수 있다.
양방향 매핑 정리
단방향 매핑 만으로도 이미 연관관계 매핑은 가능하다. 양방향 매핑은 반대 방향으로 조회(객체 그래프 탐색) 기능이 추가된 것 뿐이다. JPQL 에서 역방향으로 탐색할 일이 많다. 단방향 매핑을 잘 하고 양방향은 필요할때 추가하는게 더 좋다. (어차피 테이블 구성에 영향을 주지 않는다.)
연관관계의 주인을 정하는 기준
비즈니스 로직을 기준으로 연관관계의 주인을 선택하면 안되고, 외래키의 위치를 기준으로 정해야한다. 보통 외래키가 있는곳이 연관관계의 주인이다. 객체와 테이블의 모델링을 그림으로 한번 그려보면 외래키가 어디에 있어야 하는지 눈에 쉽게 들어오니 그려보고 판단하는게 좋아 보인다.
-
[jpa] 엔티티 매핑
intro : jpa의 엔티티 매핑에 대해 알아보자.
엔티티 매핑 소개
객체와 테이블 매핑 : @Entity, @Table
필드와 컬럼 매핑 : @Column
기본 키 매핑 : @Id
연관관계 매핑 : @ManyToOne, @JoinColumn
객체와 테이블 매핑
@Entity
@Entity가 붙은 클래스는 JPA가 관리 엔티티라 한다.
JPA를 사용해서 테이블과 매핑할 클래스는 @Entity필수.
주의사항
기본 생성자는 반드시 존재해야한다(public, protect 생성자)
final 클래스, enum, interface, inner 클래스 사용X
저장할 필드에 final 사용
@Entity 속성 정리
속성 : name
JPA에서 사용할 엔티티 이름을 지정한다. 클래스 이름을 그대로 사용.(클래스 이름을 테이블 이름으로 사용함)
@Table
@Table은 엔티티와 매핑할 테이블 지정
속성 : name ➢ 매핑할 테이블 이름
속성 : catalog ➢ 데이터베이스 catalog 매핑
속성 : schema ➢ 데이터베이스 schema 매핑
속성 : uniqueConstraints ➢ DDL 생성시에 유니크 제약 조건 생성
데이터베이스 스키마 자동 생성
DDL을 애플리케이션 실행 시점에 자동 생성. 테이블중심에서 객체중심으로 변환. 데이터베이스 방언을 활용해서 데이터베이스에 맞는 적합한 DDL 생성. 이렇게 생성된 DDL은 개발 장비에서만 사용. 생성된 DDL은 운영서버에서는 사용하지 않거나. 적절히 다듬은 후 사용
hibernate.hbm2ddl.auto의 옵션값으로 하기와 같은 다양한 값을 설정할 수 있다.
옵션 : create ➢ 기존테이블 삭제 후 다시 생성 (DROP + CREATE)
옵션 : create-drop ➢ create와 같으나 종료시점에 테이블 DROP
옵션 : update ➢ 변경분만 반영 (운영DB에는 사용하면 안됨)
옵션 : validate ➢ 엔티티와 테이블이 정상 매핑되었는지만 확인
옵션 : none ➢ 사용하지 않음
데이터베이스 스키마 자동 생성 - 주의
운영 장비에는 절대 create, create-auto, update 사용하면 안된다.
개발 초기 단계는 create 또는 update
테스트 서버는 update 또는 validate
스테이징과 운영 서버는 validate 또는 none
DDL 생성기능
제약 조건 추가 : 회원 이름은 필수 (10자 초과 X)
@Column(nullable = false, length = 10)
유니크 제약조건 추가
@Column(unique = true)
필드와 컬럼 매핑
매핑 어노테이션 정리
@Column : 컬럼 매핑
속성 : name ➢ 필드와 매핑할 테이블의 컬럼 이름
속성 : insertable, updateable ➢ 등록 변경 가능 여부
속성 : nullable ➢ null 값의 허용 여부 설정 false 설정시 not null 제약 조건이 붙음
속성 : unique ➢ 간단히 유니크 제약 조건 걸때 사용
속성 : columnDefinition ➢ 데이터베이스 컬럼 정보를 직접 줄 수 있음 (varchar(100))
속성 : length ➢ 문자 길이 제약조건, String 타입에만 사용
속성 : precision,scale ➢ BigDecimal 같은 타입에서만 사용
@Temporal : 날짜 타입 매핑
날짜 타입 (Date, Calendar)를 매핑할때 사용. LocalDate, LocalDateTIme을 사용할때는 생략 가능.
속성 : value ➢ TemporalType.DATE (날짜 데이터베이스 date 타입과 매핑)
속성 : value ➢ TemporalType.TIME (시간 데이터베이스 time 타입과 매핑)
속성 : value ➢ TemporalType.TIMESTAMP (날짜 데이터베이스 timestamp 타입과 매핑)
@Enumerated : enum 타입 매핑 (ORDINAL 사용 X)
속성 : value ➢ EnumType.STRING (enum 이름을 데이터 베이스에 저장)
속성 : value ➢ EnumType.ORDINNAL (enum 순서를 데이터 베이스에 저장)
@Lob : BLOB(바이너리 데이터), CLOB(문자 데이터) 매핑 - 큰 데이터 타입 지정할때 사용
매핑하는 필드 타입이 문자면 CLOB 매핑, 나머지는 BLOB 매핑
@Transient : 특정 필드를 컬럼에 매핑하지 않음 (매핑 무시)
필드 매핑 X
데이터베이스에 저장 X 조회 X
주로 메모리상에 임시로 어떤 값을 보관하고 싶을 때 사용
기본 키 매핑
기본 키 매핑 어노테이션
@Id, @GeneratedValue
기본 키 매핑 방법
직접 할당: @Id만 사용
자동 생성: @GeneratedValue
IDENTITY 전략 - 특징
IDENTITY : 데이터베이스에 위임 MYSQL
기본 키 생성을 데이터베이스에 위임. 주로 MYSQL, PostgreSQL, SQL Server, DB2에서 사용 AUTO_INCREMENT는 데이터베이스에 INSERT SQL을 실행한 이후에 ID 값을 알 수 있음.
예외적으로 JPA는 IDENTITY 전략을 사용하는 경우 em.persite() 하는 시점에서 미리 SQL 실행 후, 식별자 조회를 한다 그 이유는 SQL 을 실행하고나서 PK 값을 알 수 있기 떄문에 JPA 내부적으로 SQL을 선 실행하고, PK 값을 영속성 컨텍스트에 담는다.
SEQUENCE 전략 - 특징
SEQUENCE : 데이터베이스 시퀀스 오브젝트 사용, ORACLE
데이터베이스 시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트
SEQUENCE 전략 - 매핑
@SequenceGenerator
속성 : name ➢ 식별자 생성기 이름
속성 : sequenceName ➢ 데이터베이스에 등록되어 있는 시퀀스 이름
속성 : initialValue ➢ 처음 시작 값 보통 1로 지정
속성 : allocationSize ➢ 시퀀스값이 1씩 증가로 설정되어 있다면 1로 설정해야함 (성능 최적화에 사용)
속성 : catalog, schema ➢ 데이터 베이스 catalog, schema 이름
TABLE 전략 - 매핑
TABLE : 키 생성용 테이블 사용, 모든 DB에서 사용
키 생성 전용 테이블을 하나 만들어서 데이터베이스 시퀀스를 흉내내는 전략
장점 : 모든 데이터베이스에 적용 가능
단점 : 성능이 안좋음
AUTO 전략
AUTO : 방언에 따라 자동 지정, 기본값 (따로 설정 안해도 됨)
권장하는 식별자 전략
기본 키 제약 조건 : null 아님, 유일, 변하면 안된다.
미래까지 이 조건을 만족하는 자연키는 찾기 어렵다. 대리키를 사용하자.
예를들어 주민등록번호도 기본키로 적절하지 않다.
권장: Long형 + 대체키 + 키 생성전략 사용
-
[jpa] 영속성 관리(내부 동작 방식)
intro : jpa의 영속성 관리에 대해 알아보자.
JPA에서 가장 중요한 2가지
1. 객체와 관계형 데이터베이스 매핑하기
2. 영속성 컨텍스트
엔티티 매니저 팩토리와 엔티티 매니저
EntityManagerFactory
엔티티 매니저 팩토리는 JPA 애플리케이션에서 데이터베이스와의 연결을 관리하고 엔티티 매니저 객체를 생성하는 팩토리입니다. 데이터베이스와의 연결 설정, 캐싱, 그리고 여러 개의 엔티티 매니저 인스턴스를 관리하는데 사용됩니다.
EntityManager
엔티티 매니저는 JPA의 핵심 인터페이스로, 엔티티를 데이터베이스에 CRUD(Create, Read, Update, Delete) 작업을 수행하는 데 사용됩니다. 엔티티 매니저 특정 단위의 작업(예: 하나의 트랜잭션 또는 요청)을 처리할 때 사용됩니다.
영속성 컨텍스트
JPA 를 이해하는데 가장 중요한 용어, 엔티티를 영구 저장하는 환경 이라는 뜻. 영속성 컨텍스트는 논리적인 개념이며 엔티티 매니저를 통해서 영속성 컨텍스트에 접근 가능.
엔티티의 생명주기
비영속
영속성 컨텍스트와 전혀 관계가 없는 새로운 상태
Member member = new Member();
영속
영속성 컨텍스트에 관리되는 상태
em.persist(member);
준영속
영속성 컨텍스트에 저장되었다가 분리된 상태
em.detach(member);
em.clear();
em.close();
삭제
삭제된 상태
em.remove(member)
영속성 컨텍스트의 이점
1차 캐시
엔티티 매니저는 자체적으로 1차 캐시를 갖고 있습니다. 같은 엔티티 매니저 내에서 동일한 엔티티를 여러 번 조회하면, 데이터베이스에 다시 쿼리를 보내지 않고 1차 캐시에서 반환합니다.
동일성 보장
동일한 엔티티 매니저 내에서 같은 엔티티를 여러 번 조회하면 동일한 객체 인스턴스가 반환됩니다.
트랜잭션을 지원하는 쓰기 지연
엔티티 매니저는 persist()나 merge() 등으로 데이터를 수정해도 즉시 데이터베이스에 반영하지 않고, 트랜잭션이 커밋될 때 한꺼번에 SQL 쿼리를 보냅니다.
변경 감지
엔티티 매니저는 엔티티의 변경 사항을 자동으로 감지하고, 트랜잭션이 커밋될 때 변경된 내용을 데이터베이스에 반영합니다. (스냅샷과 비교하여 변경을 감지함)
지연 로딩
엔티티의 연관된 데이터가 실제로 필요할 때까지 데이터베이스 조회를 지연시킵니다. 위 특징으로 인해 불필요한 데이터 로드를 피하고 성능을 최적화할 수 있습니다.
플러시
영속성 컨텍스트의 변경 내용을 데이터 베이스에 반영. 이때 영속성 컨텍스트를 비우지않음. 영속성 컨텍스트의 변경 내용을 데이터베이스에 동기화. 트랜잭션이라는 작업단위가 중요하며, 커밋 직전에만 동기화 하면 됨.
영속성 컨텍스트를 플러시 하는 방법
em.flush() 직접 호출
트랜잭션 커밋 플러시 자동 호출
JPQL 쿼리 실행 플러시 자동 호출
JPQL 쿼리 실행시 플러시가 자동으로 호출되는 이유
JPQL 실행 시 자동 플러시는 쿼리가 실행되기 전에 EntityManager의 변경 사항이 반영되도록 하여, JPQL의 결과가 데이터의 최신 상태를 보장하도록 돕는 중요한 동작입니다.
Touch background to close