JPA 값 타입

zwon·2023년 10월 4일
0

JPA

목록 보기
8/9

JPA는 크게 2가지 데이터 타입으로 나눌 수 있다.

  • 엔티티 타입
    • @Entity로 정의한 객체
    • 데이터가 변해도 PK같은 식별자로 추적이 가능하다.
  • 값 타입
    • int, Integer, String처럼 자바 기본 타입이나 객체
    • 데이터가 변하면 식별자가 없어 추적이 불가능하다.

값 타입은 또 다음과 같이 분류할 수 있다.

  • 기본값 타입
    • 자바 기본 타입
    • 래퍼 클래스
    • String
  • 임베디드 타입
    • JPA에서 정의해서 사용해야함
  • 컬렉션 값 타입
    • JPA에서 정의해서 사용해야함

  • 타입에 대해 보다 더 자세히 알아보자.

기본 값 타입

  • String, int 와 같은 타입들
  • 생명주기를 엔티티의 의존한다.
    • 예시로 Member 삭제 시 이름과 나이가 함께 삭제됨
  • 값 타입은 공유하면 안된다.
    • Member1의 이름 필드 변경 시 Member2의 이름 필드가 변경되면 안됨.

임베디드 타입

  • 새로운 값 타입을 직접 정의
  • 주로 기본 값 타입을 모아서 만들어서 복합 값 타입이라고도 한다.
  • 예를 들어 Member의 집 주소를 저장할 때 Member 클래스에 도시,우편번호 등의 필드를 따로 생성할 수 있지만 그냥 Address타입을 가지게 하면 가독성이 훨 좋을 것이다.
  • @Embeddable : 값 타입을 정의하는 곳에
  • @Embedded : 값 타입을 사용하는 곳에
  • 그리고 기본 생성자는 필수.

[임베디드 타입 예시]

@Embeddable // 임베디드 타입 정의
public class Address {

    private String city;
    private String street;
    private String zipcode;

    public Address() {}
    ...
}
@Entity
public class Member extends BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "MEMBER_ID")
    private Long id;

    @Column(name="name", nullable = false)
    private String username;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn
    private Team team;

    // 임베디드 타입 사용
    @Embedded 
    private Address address;
    ...
}
  • 그런데 만약 Member가 Address타입을 2개 가지고 있다면?
  • 이럴땐 컬럼명이 중복되기 때문에 @AttributeOverrrides를 사용해서 속성을 재정의해야한다.
@Entity
public class Member extends BaseEntity {
	...
	
    // 임베디드 타입 사용
    @Embedded 
    private Address homeAddress;

    @Embedded 
    @AttributeOverrrides({
          @AttributeOverrride(name="city",
          column=@Column("work_city")
          @AttributeOverrride(name="street",
          column=@Column("work_street")
          @AttributeOverrride(name="zipcode",
          column=@Column("work_zipcode"))
    })
    private Address workAddress;
    ...
}
  • 임베디드 타입을 사용하면 응집도도 높아지고 재사용성도 좋아지지만 입베디드 타입을 여러 엔티티에서 공유하면 위험하다.
  • 그래서 임베디드 타입의 값을 복사해서 사용하는 것을 추천한다.
  • 하지만 정의한 임베디드 타입은 객체여서 참조 값에 직접 값을 대입하는 것은 막을 수 없다는 한계가 있다.
  • 그래서! 객체 타입을 수정할 수 없도록 생성자로만 값을 설정하고 setter로 수정할 수 없게 하면된다. 즉 setter를 생성하지 않으면 된다.
  • 이렇게 하면 불변 객체가 괸다.
  • 또는 setter의 접근 제어자를 private으로 변경.
  • 주의해서 잘 사용하자...

컬렉션 값 타입

  • 값 타입을 컬렉션에 넣어서 사용하는 타입
    • ex) List<Address> addressHistory = ...
  • DB에서는 value만 넣을 수 있지 컬렉션을 담을 구조가 없다.
  • 그래서 보통 별도의 컬렉션 테이블을 생성해야한다.
  • 별도의 컬렉션 테이블은 식별자 PK를 따로 가지지않고 모든 컬럼을 묶어 PK로 구성한다. 당연히 null 입력 X, 중복 저장 X다.

[값 타입 컬렉션 예시]

@Entity
public class Member {
	...
    
    @ElementCollection
    @CollectionTable(name="FAVORITE_FOOD", joinColumns =
    @JoinColumn(name="member_id"))
    @Column(name="food_name")
    private Set<String> favoriteFoods = new HashSet<>();

    @ElementCollection
    @CollectionTable(name="ADDRESS", joinColumns =
    @JoinColumn(name="member_id"))
    private List<Address> addressHistory = new ArrayList<>();
    
    ...
}

[DB 테이블 생성 결과]


직접 작성해보고 생성결과를 봐야 더욱 와닿는 것 같다.
특히 값 타입 수정부분은 나가는 쿼리를 직접 보는 것을 추천한다.

값 타입은 식별자 개념이 없다고 위에서 언급했다.
그래서 값이 변경될 경우 추적이 어려워서 값 타입 컬렉션을 수정할 경우 주인 엔티티와 연관된 모든 데이터를 삭제하고 현재 값 타입 컬렉션에 있는 값들을 모두 다시 저장한다........

참고로 값 타입 컬렉션은 영속성 전이와 고가 객체 제거 기능을 필수로 가진다고 볼 수 있다.
그리고 지연 로딩 전략을 사용한다.

값 타입 컬렉션을 사용할 때 매우매우 생각을 많이 해보고 ,,, 꼭 사용해야하는지 고민하자. 일대다 관계로 풀어낼 수 있으면 일대다 관계로 풀어내자


자바 ORM 표준 JPA 프로그래밍-기본편을 학습하면서 정리한 블로그입니다.

profile
Backend 관련 지식을 정리하는 Back과사전

0개의 댓글