본문 바로가기

FastCampus - 한번에 끝내는 Java|Spring 웹 개발/04 JPA

JPA Ch 10 @Embeddable - 패스트캠퍼스 챌린지 41일차

이번에 학습한 내용은 @Embeddable / @Embedded 필드입니다.

아, 오늘도 환경 설정 문제로 IDE에서 코드를 검증해보지는 않았습니다. 원격으로 작업하기 너무 번거로워서; 손코드인 점 양해 바랍니다. 내일은 원격이 아니라 코드를 복붙해서 진행해봐야겠네요. macOS에서 DB 쓰기 번거로운데... docker도 그렇고...

@Embeddable / @Embedded

개념은 이렇습니다. 엔티티의 여러 필드를 묶어서 하나의 클래스로 다루겠다는 거구요. 거꾸로 생각해보자면 Embeddable 인 객체의 모든 필드들을 특정 @Entity 의 필드에 포함시키는 거라고 볼 수 있습니다. 그래서 어노테이션 이름이 @Embeddable / @Embedded 인 거겠죠.

문서와 함께 각 어노테이션의 의미를 살펴봅시다.

  • @Embeddable: 특정 객체가, 다른 @Entity 의 내용으로서 결합될 수 있다고 표시합니다. 클래스에 표시합니다.
    Specifies a class whose instances are stored as an intrinsic part of an owning entity and share the identity of the entity.
    • 다른 엔티티에 녹여낼 개별 필드에도 @Column() 등의 어노테이션으로 칼럼 이름을 변경하거나 하는 등의 작업을  @Entity와 똑같이 할 수 있습니다.
    • 프리픽스 없을까...? 강의에서는 안 나왔는데... 확인을 못 해보니 좀 답답하네요.
      칼럼 이름이 따로 바뀌지는 않습니다.
    • 문서에 어떤 상황에서 써야하는지에 대한 예시가 많습니다.
  • @Embedded: 특정 엔티티의 필드가, 사실은 여러개의 칼럼을 나타내고, 그 칼럼들의 정보는 필드의 필드들로부터 이 엔티티에 녹여낸다... 말로 표현하려니 어렵네요.
    뭏든 @Entity의 필드 중 타입이 @Embeddable 인 필드에 달아줘야 합니다.
    Specifies a persistent field or property of an entity whose value is an instance of an embeddable class.
    • 같은 클래스 두 개를 @Embedded 로 한 엔티티에 녹이려고 할 때, 칼럼 이름이 겹치는 문제가 발생합니다. @AttributeOverrides 와 @AttributeOverride 로 개별 필드의 칼럼 이름을 엔티티 쪽에서 바꿔줄 수 있습니다... 있는데요... 좀 깔끔하지가 않네요.

직접 해보기

코드를 테스트해볼수는 없는 관계로 결과 SQL을 확인해볼 수 없는 점이 아쉽네요. 아무튼 실제로 이걸 어떻게 쓰는지도 정리는 해놔야하니.

두 가지 정도의 사례를 들어주셨는데요, 하나는 여러 종류의 가격이 묶여있는 사례고 (적정가 / 표시가 / 최저가 ...) 나머지 하나는 외국 사이트에서 회원가입할 때 종종 주소를 시 동 면 상세주소 이렇게 세부적으로 나누는 사례인데, 이번에는 뒷내용에 해당하는 "주소"를 기준으로 잡고 진행했습니다.

@Embeddable
public class Address {
  public String city;
  public String district;
  @Column(name="address_detail")
  public String detail;
  public String zipCode
}

그리고 User 클래스에 필드로 넣을거구요. 사용자의 주소는 집주소와 회사주소로 나눌수도 있으니, 같은 엔티티에 임베딩된 객체가 둘 이상인 경우도 한 번에 확인해볼 수 있을겁니다.

생략
@Entity
public class User {
    생략
    
    @Embedded
    public Address homeAddress;
    
    @Embedded
    @AttributeOverrides({
      @AttributeOverride(name = "city", column = @Column(name="company_city"),
      같은 방식으로 나머지 필드도 이름을 바꿔줌
    })
    public Address companyAddress;
}

 

SQL 의 DDL은 다음과 같이 나옵니다. 실제 SQL을 확인할 수는 없으니 필드 목록만 나열해보겠습니다.

  • 엔티티 User의 테이블상의 칼럼들
    • ID (생략했었음)
    • 그 외 필드
    • city
    • district
    • address_detail
    • zip_code
    • company_city
    • company_district
    • company_address_detail
    • company_zip_code

Address 클래스의 필드들이 User 엔티티에 납작하게 눌려서 들어갔습니다.

Address가 null이던, Address의 필드가 모두 null이던 같은 의미

그런데 한 가지 궁금할만한 점은 다음 두 가지의 차이점입니다.

  • User.homeAddress = new Address(null, null, 전부 null)
    이 후 저장
  • User.homeAddress = null
    그냥 Address인 필드를 아예 null로 지정
    이 후 저장

위의 예상되는 필드 목록에서 보셨다시피, 이 두개는 사실 차이가 없습니다. 그럼에도 불구하고 강의에서 이걸 확인하기 위해 코드를 짜봤을 때에는 new Address()를 만들어서 save 한 뒤 findAll() 로 다시 찾아왔을 때에도 여전히 이 객체만 Address가 있었습니다.

@Test
void testEmbeddedToNull() {
  User user1 = new User();
  user1.name = "user1";
  user1.homeAddress = new Address(null, null, null, null);
  
  User user2 = new User();
  user2.name = "user2";
  user2.homeAddress = null;
  
  userRepo.save(user1);
  userRepo.save(user2);
  userRepo.flush();
  
  // entityManager.clear();
  
  List<User> users = userRepo.findAll();
  users.forEach(System::out::println);
}

이는 엔티티 매니저에 1차 캐시로 객체가 저장되어 있기 때문으로, 위 샘플 코드에 주석을 쳐놓은 부분처럼 em.clear() 를 호출해주면 user1 과 user2 가 동등하게 homeAddress 가 null 로 나오는 점을 확인하실 수 있습니다.

본 포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성되었습니다.
패스트캠퍼스: https://bit.ly/37BpXiC

#패스트캠퍼스 #패캠챌린지 #직장인인강 #직장인자기계발 #패스트캠퍼스후기 #한번에끝내는JavaSpring웹개발마스터초격차패키지Online

담백...

다음 강의가 트러블슈팅이라고 하시던데 실습 환경이 필요하다는 생각이 들면 잠깐 미루고 스프링 시큐리티 강의를 먼저 봐야할지도 모르겠습니다.