김영한의 ORM 표준 JPA 프로그래밍(기본편) - 값타입, 임베디드 타입, 값 타입 컬렉션

2025. 2. 8. 01:16·김영한의 ORM 표준 JPA 프로그래밍(기본편)

Qestion

  • 임베디드 타입의 장점은?
  • 엔티티 안에 같은 임베디드 타입이 여러개라면 무슨 문제가 발생하고 어떻게 해결하는가?
  • 임베디드 타입을 사용할 때 주의점과, 어떻게 문제를 방지할 수 있는가?
  • 값 타입 컬렉션의 문제점은?
  • 값 타입 컬렉션의 대안은?
  • 엔티티 타입과 값 타입의 주요 차이점은?

 

JPA 값 타입 분류

  • 기본 값 타입
    • 생명주기를 엔티티에 의존(회원을 삭제하면 이름, 나이 필드도 함께 삭제)
    • 값 타입은 공유하면 안됨(회원 이름을 변경시 다른 회원의 이름도 함께 변경되면 안됨)
    • 자바 기본 타입(int, double)
    • 래퍼 클래스(Integer, Long)
    • String
  • 임베디드 타입
    • 새로운 값 타입을 직접 정의할 수 있음
    • 주로 기본 값 타입을 모아 만들기 때문에 복합 값 타입이라고도 함
  • 컬레션 값 타입
    • List, Set 등

 

임베디드 타입

  • city, street, zipcode와 같은 서로 연관 있는 String들을 묶어서 Address라는 임베디드 타입을 생성
  • 임베디드 타입도 값 타입의 한 종류
  • 사용법
    • @Embeddable: 값 타입을 정의하는 곳에 표시
    • @Embedded: 값 타입을 사용하는 곳에 표시
    • 기본 생성자 필수로 생성
  • 장점
    • 재사용성
      • Address 를 만들어 Member에 사용할 수도 있고, 추후에 다른 Manager와 같은 클래스에도 사용 가능
      • Address 하위에 util 함수를 만들거나, zipcode에 length를 걸거나 하는 것도 여러 곳에서 계속 재사용됨
    • 높은 응집도
      • 서로 연관 있는 것들 끼리 묶어 응집도가 높아짐
    • 임베디드 타입을 포함한 모든 값 타입은, 값 타입을 소유한 엔티티에 생명주기를 의존함(Cascade.ALL, orphanRemoval = true)
  • 테이블 맵핑
    • 임베디드 타입은 사실상 테이블 입장에서는 그냥 값일 뿐
    • 임베디드 타입을 사용하기 전과 후에 맵핑 테이블은 사실 똑같음
    • 객체와 테이블을 아주 세밀하게 맵핑 가능
    • 잘 설계한 ORM 애플리케이션은 맵핑한 테이블의 수보다 클래스의 수가 더 많음
    • 임베디드 타입 아래 또 Entity가 있을 수도 있음
  • 임베디드 타입의 값이 null 이면 맵핑한 컬럼 값도 모두 null

 

 

@AttributeOverride

@Entity
public class Member{
	@Id
	private String MEMBER_ID;
    
	@Embedded
	private Address homeAddress;
    
	@Embedded
	@AttributeOverrides({
		@AttributeOverride(name="city"
			column=@Column("WORK_CITY")),
		@AttributeOverride(name="street"
			column=@Column("WORK_STREET")),
		@AttributeOverride(name="zipcode"
			column=@Column("WORK_ZIPCODE"))
	})
	private Address workAddresss;
	...
}
  • 한 엔티티 내에서 같은 임베디드 타입이 있는 경우 컬럼 명이 중복되는 문제 발생
  • @AtributeOverrides, @AttributeOverride를 통해 컬럼명 속성을 재정의 가능

 

 

객체 타입의 한계

  • 임베디드 타입은 값 타입이지만 객체 타입임
  • 객체 타입이라는 말은 참조 값을 사용한다는 것이고, 참조 값을 사용하면 값이 공유될 수 있음
  • 따라서, 값을 공유하면 안되고 값을 복사해서 새로 만들어서 사용해야 함
  • 혹은, 객체 타입을 수정할 수 없게 만들면 부작용을 원천 차당 가능
  • 값 타입은 불변 객체로 설계하는게 원칙
  • 생성자로만 값을 설정하고 수정자는 만들지 않으면 됨
  • Integer, String같은 것이 자바가 제공하는 대표적인 불변 객체

 

값 타입 컬렉션

@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ElementCollection
    @CollectionTable(name = "ADDRESS", joinColumns = @JoinColumn(name = "MEMBER_ID"))
    private List<Address> addres = new ArrayList<>();
    
	...
}
  • 정규화 DB는 데이터를 하나 넘게 넣을 수 없음
  • 따라서, 컬렉션 같은 데이터를 넣을 수 없어 컬렉션 저장을 위한 테이블이 필요함
  • @ElementCollection, @CollectionTable을 사용하면, 자동으로 테이블을 생성해줌
  • 테이블이므로 디폴트로 지연 로딩 사용됨
  • 이 또한 값 타입이므로 Cascade.ALL + orphanRemoval = true

 

값 타입 컬렉션의 제약사항

  • 엔티티와 다르게 식별자 개념이 없음.
  • 자동으로 속성과 부모의 Primary Key를 만들어주지만 별도의 식별자가 없음
  • 따라서 값 변경에 대한 추적이 어려움
  • 값 타입 컬렉션에 변경 사항이 발생하면, 부모 엔티티와 연관된 모든 데이터를 삭제하고, 기존+변경 데이터를 다시 넣는다
    • 값 타입 컬렉션은 식별자가 없는 단순한 값으로만 이루어져 각 값을 추적하기 어려움
    • 이러한 방법은 매우 비효율적
  • 모든 컬럼을 묶어 Primary Key를 구성해야 함.
  • 따라서 null 입력 X, 중복 저장 X

 

 

값 타입 컬렉션의 대안

  • 실무에서는 상황에 따라 값 타입 컬렉션 대신 일대다 관계를 고려
  • 일대다 엔티티를 만들고 여기에 값 타입을 사용
  • Cascade + 고아 객체로 값 타입 컬렉션처럼 사용
  • 별도의 Primary Key를 만들 수 있고 기존 값 타입 컬렉션의 문제점을 해결 가능
  • 값 타입 컬렉션은 정말 정말 간단한 케이스에서만 가끔 사용은 가능함

 

정리

  • 엔티티 타입 특징
    • 식별자가 있음
    • 생명 주기를 스스로 관리
    • 공유 가능
  • 값 타입의 특징
    • 식별자가 없음
    • 생명 주기를 엔티티에 의존
    • 공유하지 않는 것이 안전(복사해서 사용)
    • 불변 객체로 만드는 것이 안전
  • 엔티티 타입은 공유 가능하고 값 타입은 그렇지 않은 이유
    • 엔티티 타입은 DB 상에서 유일하게 식별 되므로, 여러 곳에서 공유 가능
    • 값 타입은 유일하지도 않고 식별자도 없으며 값 자체를 의미하므로 공유할 필요도 없음
  • 식별자가 필요하고, 지속해서 값 추적이 필요하고, 변경이 필요하다면 그것은 값 타입이 아닌 엔티티로 선언

'김영한의 ORM 표준 JPA 프로그래밍(기본편)' 카테고리의 다른 글

김영한의 ORM 표준 JPA 프로그래밍(기본편) - JPQL  (0) 2025.02.08
김영한의 ORM 표준 JPA 프로그래밍(기본편) - 프록시, 지연로딩, Cascade, 고아 객체  (1) 2025.02.07
김영한의 ORM 표준 JPA 프로그래밍(기본편) - 상속관계 맵핑과 - @MappedSuperclass  (1) 2025.02.06
김영한의 ORM 표준 JPA 프로그래밍(기본편) - 다대일, 일대다, 일대일, 다대다  (1) 2025.02.05
김영한의 ORM 표준 JPA 프로그래밍(기본편) - 연관관계 단방향 맵핑, 양방향 맵핑  (1) 2025.02.05
'김영한의 ORM 표준 JPA 프로그래밍(기본편)' 카테고리의 다른 글
  • 김영한의 ORM 표준 JPA 프로그래밍(기본편) - JPQL
  • 김영한의 ORM 표준 JPA 프로그래밍(기본편) - 프록시, 지연로딩, Cascade, 고아 객체
  • 김영한의 ORM 표준 JPA 프로그래밍(기본편) - 상속관계 맵핑과 - @MappedSuperclass
  • 김영한의 ORM 표준 JPA 프로그래밍(기본편) - 다대일, 일대다, 일대일, 다대다
5jyan5
5jyan5
  • 5jyan5
    jyan
    5jyan5
  • 전체
    오늘
    어제
    • 분류 전체보기 (242)
      • 김영한의 스프링 핵심 원리(기본편) (8)
      • 김영한의 스프링 핵심 원리 - 고급편 (11)
      • 김영한의 스프링 MVC 1편 (1)
      • 김영한의 스프링 DB 1편 (3)
      • 김영한의 스프링 MVC 2편 (3)
      • 김영한의 ORM 표준 JPA 프로그래밍(기본편) (9)
      • 김영한의 스프링 부트와 JPA 활용2 (2)
      • 김영한의 실전 자바 - 중급 1편 (1)
      • 김영한의 실전 자바 - 고급 1편 (9)
      • 김영한의 실전 자바 - 고급 2편 (9)
      • Readable Code: 읽기 좋은 코드를 작성.. (2)
      • 김영한의 실전 자바 - 고급 3편 (9)
      • CKA (118)
      • 개발 (37)
      • 경제 (4)
      • 리뷰 (1)
      • 정보 (2)
  • 블로그 메뉴

    • 링크

    • 공지사항

    • 인기 글

    • 태그

      WAS
      단방향 맵핑
      @discriminatorvalue
      스레드
      Target
      페치 조인
      @within
      typequery
      김영한
      gesingleresult
      reentarantlock
      hibernate5module
      빈 후처리기
      JPQL
      @discriminatorcolumn
      프록시
      양방향 맵핑
      jdk 동적 프록시
      버퍼
      자바
      cglib
      프록시 팩토리
      조회 성능 최적화
      고급
      락
      @args
      jpq
      requset scope
      Thread
      log trace
    • 최근 댓글

    • 최근 글

    • hELLO· Designed By정상우.v4.10.2
    5jyan5
    김영한의 ORM 표준 JPA 프로그래밍(기본편) - 값타입, 임베디드 타입, 값 타입 컬렉션
    상단으로

    티스토리툴바