ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [JPA] @ElementCollection을 이용해서 Embeddable 타입의 Collection을 영속화
    스프링프레임워크/jpa 2021. 1. 29. 15:28

    Embeddable 타입의 Collection

    • 엔티티 클래스에서 Embeddable 타입의 Collection은 @ElementCollection을 사용해서 영속화 할 수 있습니다.
    • Collection에 해당하는 클래스에는 @Embeddable 어노테이션을 반드시 붙여야 합니다.
    • 엔티티 클래스와 Embeddable 타입의 클래스는 둘 사이를 매핑하는 새로운 테이블을 생성합니다.
      • 해당 매핑 테이블에는 엔티티 클래스의 primary-key와 연결 된 foreign-key를 가지고 있습니다.
      • 해당 매핑 테이블의 필드는 엔티티 클래스에 대한 foreign-key와 Embeddable 클래스의 필드들로 구성되어집니다.
    • OneToMany와 유사하지만 대상이 Embeddable 클래스란 것이 다르다.

    테스트를 위한 Entity 및 관련 클래스 작성

    @NoArgsConstructor(access = AccessLevel.PROTECTED)
    @Getter
    @Entity
    public class Customer {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        private String name;
        @ElementCollection
        private List<PhoneNumber> phoneNumbers = new ArrayList<>();
    
        public Customer(String name, PhoneNumber... phoneNumbers) {
            this.name = name;
            for (PhoneNumber phoneNumber : phoneNumbers) {
                this.phoneNumbers.add(phoneNumber);
            }
        }
    }
    @Getter
    @NoArgsConstructor
    @Embeddable
    public class PhoneNumber {
        private String phoneNumber;
        @Enumerated(EnumType.STRING)
        private PhoneType type;
    
        public PhoneNumber(String phoneNumber, PhoneType type) {
            this.phoneNumber = phoneNumber;
            this.type = type;
        }
    }
    public enum PhoneType {
        House, Work, Cell
    }
    public interface CustomerRepository extends JpaRepository<Customer, Long> {
    }

     

     

    엔티티에 의해서 생성되어지는 테이블의 구조는 아래와 같습니다.

    customer 테이블

    CREATE TABLE `customer` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      `name` varchar(255) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

    customer_phone_numbers 테이블 (매핑 테이블)

    CREATE TABLE `customer_phone_numbers` (
      `customer_id` bigint(20) NOT NULL,
      `phone_number` varchar(255) DEFAULT NULL,
      `type` varchar(255) DEFAULT NULL,
      KEY `FK8t59yk70tp1u41ltrlfkmk4ut` (`customer_id`),
      CONSTRAINT `FK8t59yk70tp1u41ltrlfkmk4ut` FOREIGN KEY (`customer_id`) REFERENCES `customer` (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

     

     

    테스트 케이스 작성

    우선 Customer를 등록 시 실행되는 SQL을 살펴보겠습니다.

    • customer 테이블에 1개의 row 저장
    • customer_phone_numbers 테이블에 2개의 row 저장
      • Collection의 개수만큼 자동으로 INSERT
    @Test
    @Order(1)
    public void 고객등록() {
        Customer customer = new Customer("장지현", new PhoneNumber("0317091628", PhoneType.House), new PhoneNumber("01047071628", PhoneType.Cell));
        Customer savedCustomer = customerRepository.save(customer);
        customerId = savedCustomer.getId();
    }

     

    Customer 조회 시 실행되는 SQL을 살펴보겠습니다.

    • customer 테이블에서 1회 조회
    • Collection 필드를 참조하는부분을 주석처리해서 customer_phone_numbers 테이블을 조회 하는 부분이 없습니다.
    @Test
    @Order(2)
    public void 고객검색() {
        customerRepository.findById(customerId).ifPresent(e -> {
    	    System.out.println("ID : " + e.getId());
    	    System.out.println("Name : " + e.getName());
    	    /* for (PhoneNumber phoneNumber : e.getPhoneNumbers()) {
    	      System.out.println(phoneNumber.getPhoneNumber());
    	    } */
        });
    }

     

    이번에는 주석을 풀어서 Collection 필드를 참조할 경우 실행되는 SQL을 살펴보겠습니다.

    • customer 테이블에서 1회 조회
    • customer_phone_numbers 테이블에서 1회 조회
    • PhoneNumber의 필드 값을 참조할 때 Lazy 로딩에 의해서 PhoneNumber 값을 SELECT함
    • OneToMany와 차이점
      • PhoneNumber는 Customer 엔티티를 통해서만 조회 가능
      • PhoneNumber만 삭제 불가능
    @Test
    @Order(2)
    public void 고객검색() {
        customerRepository.findById(customerId).ifPresent(e -> {
    	    System.out.println("ID : " + e.getId());
    	    System.out.println("Name : " + e.getName());
    	    for (PhoneNumber phoneNumber : e.getPhoneNumbers()) {
    	      System.out.println(phoneNumber.getPhoneNumber());
    	    }
        });
    }

     

    Customer를 2개 INSERT 한 후 전체 조회 시 SQL

    • PhoneNumber 정보에 대해서 IN 절로 검색

     

     

    Customer 삭제 시 실행되는 SQL

    • customer 테이블에서 1회 삭제
    • customer_phone_numbers 테이블에서 1회 삭제
    @Test
    @Order(3)
    public void 고객삭제() {
        customerRepository.deleteById(customerId);
    }

    댓글

Designed by Tistory.