Back-end/JPA

자바 ORM 표준 JPA 기본값 타입, 임베디드 타입

이안_ian 2023. 6. 1. 18:41
반응형

JPA의 데이터 타입 분류

엔티티 타입

  • @Entity로 정의하는 객체
  • 데이터가 변해도 식별자로 지속해서 추적 가능
  • 예) 회원 엔티티의 키나 나이 값을 변경해도 식별자로 인식 가능

값 타입

  • int, Integer, String처럼 단순히 값으로 사용하는 자바 기본 타입이나 객체
  • 식별자가 없고 값만 있으므로 변경시 추적 불가
  • 예) 숫자 100을 200으로 변경하면 완전히 다른 값으로 대체
  • 종류로는 기본값 타입, 임베디드 타입, 컬렉션 값 타입이 존재

기본값 타입

String name, int age 등 이며, 생명주기를 엔티티의 의존한다. (회원을 삭제하면 이름, 나이도 함께 삭제됨)

값 타입은 공유하면 안된다. 회원 이름 변경 시 다른 회원의 이름도 함께 변경되면 안됨

자바의 기본 타입은 절대 공유가 안된다.

int, double 같은 기본 타입(primitive type)은 절대 공유X

기본 타입은 항상 값을 복사함

Integer같은 래퍼 클래스나 String 같은 특수한 클래스는 공유 가능 객체이지만 변경X

임베디드 타입

새로운 값 타입을 직접 정의할 수 있음

JPA는 임베디드 타입이라함

주로 기본값 타입을 모아서 만들어서 복합 값 타입이라고도함

int, String과 같은 값 타입

 

예시)

회원 엔티티는 이름, 근무 시작일, 근무 종료일, 주소 도시, 주소 번지, 주소 우편번호를 가진다.

Member
id
name
startDate
endDate
city
street
zipcode

 

위에서 자주쓰일 법한 컬럼들을 따로 관리하여 좀더 간결하게 아래와 같이 정의할 수 있다.

회원 엔티티는 이름, 근무 기간, 집 주소를 가진다.

Member
id
name
workPeriod
homeAddress

임베디드 타입 설정 방법

@Entity
public class Member {
  @Id
  @GeneratedValue
  private Long id;
  
  private String username;
  
  @Embedded
  private Period workPeriod;
  
  @Embedded
  private Address homeAddress;
}
@Embeddable
public class Period {
  private LocalDateTime startDate;
  private LocalDateTime endDate;
}
@Embeddable
public class Address {
  private String city;
  private String street;
  private String zipcode;
}

임베디드 타입과 테이블 매핑

  • 임베디드 타입은 엔티티의 값일 뿐이다.
  • 임베디드 타입을 사용하기 전과 후에 매핑하는 테이블은 같다.
  • 객체와 테이블을 아주 세밀하게 매핑하는 것이 가능
  • 잘 설계한 ORM 애플리케이션은 매핑한 테이블의 수보다 클래스의 수가 더 많음

@AttributeOverride : 속성 재정의

한 엔티티에서 같은 값 타입을 사용하면?

컬럼 명이 중복됨, @AttributeOverride 또는 @AttributeOverrides를 사용해서 컬럼 명 속성을 재정의

현재 Member에는 집주소만 정의했지만 직장주소도 매핑하고 싶을 수 있다. 그렇게되면 똑같은 컬럼명이 들어가기 때문에 에러가 발생되기에 위 옵션을 사용해야한다.

@Entity
public class Member {
  중략..
  
  @Embedded
  private Address homeAddress;
  
  @Embedded
  @AttributeOverrides({
    @AttributeOverride(name = "city", column=@Column(name = "work_city")),
    @AttributeOverride(name = "street", column=@Column(name = "work_street")),
    @AttributeOverride(name = "zipcode", column=@Column(name = "work_zipcode"))
  })
  private Address workAddress;
}

그러면 city, street, zipcode, work_city, work_street, work_zipcode가 모두 생성된다.

값 타입 공유 참조

임베디드 타입 같은 값 타입을 여러 엔티티에서 공유하면 위험함, side effect 발생

Address address = new Address("city", "street", "1000");

Member member1 = new Member();
member.setUsername("member1");
member.setHomeAddress(address);
em.persist(member1);

Member member2 = new Member();
member.setUsername("member2");
member.setHomeAddress(address);
em.persist(member2);

member1.getHomeAddress().setCity("newCity");

이렇게 할 경우 member1, member2 모두 수정된다.

값 타입 복사

값 타입의 실제 인스턴스인 값을 공유하는 것은 위험

대신 값(인스턴스)를 복사해서 사용

Address address = new Address("city", "street", "1000");

Member member1 = new Member();
member.setUsername("member1");
member.setHomeAddress(address);
em.persist(member1);

Address copyAddress = new Address(address.getCity(), address.getStreet(), address.getZipcode());

Member member2 = new Member();
member.setUsername("member2");
member.setHomeAddress(copyAddress);
em.persist(member2);

member1.getHomeAddress().setCity("newCity");

객체 타입의 한계

  • 항상 값을 복사해서 사용하면 공유 참조로 인해 발생하는 부작용을 피할 수 있다.
  • 문제는 임베디드 타입처럼 직접 정의한 값 타입은 자바의 기본 타입이 아니라 객체 타입이다.
  • 자바 기본 타입에 값을 대입하면 값을 복사한다.
  • 객체 타입은 참조 값을 직접 대입하는 것을 막을 방법이 없다.
  • 객체의 공유 참조는 피할 수 없다.

그래서 불변 객체로 만들어야 위와 같은 상황을 예방 할 수 있다. 이런 에러가 발생 시 디버깅이 상당히 어려움

불변객체

  • 객체 타입을 수정할 수 없게 만들면 부작용을 원천 차단
  • 값 타입은 불변 객체로 설계해야함
  • 불변 객체 : 생성 시험 이후 절대 값을 변경할 수 없는 객체
  • 생성자로만 값을 설정하고 수정자(Setter)를 만들지 않으면 됨
  • 참고: Integer, String은 자바가 제공하는 대표적인 불변 객체

 

반응형