본문 바로가기
Everyday Study

2024.09.19(목) { 컬렉션 값 타입, CROSS JOIN }

by xogns93 2024. 9. 19.

컬렉션 값 타입은 JPA(Java Persistence API)에서 값 타입(Value Type)을 컬렉션으로 사용하는 개념을 의미합니다. 즉, 값 타입이 여러 개 모여서 하나의 엔티티에서 관리될 때, 이를 컬렉션 값 타입이라고 합니다. 주로 Embeddable로 정의된 값 타입을 여러 개 관리할 수 있도록 컬렉션 형태로 사용합니다.

컬렉션 값 타입의 특징

  1. 값 타입(Value Type):
    • 값 타입은 엔티티가 아닌, 데이터 그 자체를 의미하는 타입입니다. 기본 값 타입(예: String, Integer)이나, 임베디드 타입(@Embeddable로 정의된 클래스)이 있습니다.
    • 값 타입은 엔티티와는 다르게 식별자(ID)가 없으며, 동일한 값을 가지면 동일한 것으로 간주됩니다.
  2. 컬렉션으로 관리:
    • 여러 값 타입 객체를 한 엔티티가 관리할 수 있습니다. 이를 컬렉션으로 관리할 때 주로 List, Set, Map과 같은 컬렉션 자료구조를 사용합니다.
  3. 데이터베이스 테이블 매핑:
    • 컬렉션 값 타입은 데이터베이스의 별도의 테이블에 저장됩니다. 주 엔티티와 1:N 관계처럼 보일 수 있으며, 컬렉션 값 타입을 저장하는 테이블은 외래 키로 주 엔티티의 식별자를 참조합니다.
  4. 생명주기 공유:
    • 값 타입은 해당 엔티티의 생명주기에 종속됩니다. 즉, 엔티티가 삭제되면 그에 포함된 컬렉션 값 타입도 함께 삭제됩니다.
  5. 변경 불가:
    • 값 타입 객체는 불변 객체로 설계하는 것이 좋습니다. 즉, 생성 후 수정하지 않고, 새로 생성하여 값을 변경합니다.

예시 코드

다음은 Address라는 값 타입을 컬렉션 값 타입으로 사용하는 예시입니다.

1. 값 타입 클래스 (@Embeddable로 정의)

import javax.persistence.Embeddable;

@Embeddable
public class Address {
    private String city;
    private String street;
    private String zipcode;

    // 기본 생성자, 게터, 세터
    public Address() {}

    public Address(String city, String street, String zipcode) {
        this.city = city;
        this.street = street;
        this.zipcode = zipcode;
    }

    // 불변 객체로 사용하기 위해 Setter 대신 생성자로 값 설정

    public String getCity() {
        return city;
    }

    public String getStreet() {
        return street;
    }

    public String getZipcode() {
        return zipcode;
    }

    // equals, hashCode 메서드 구현 (값 타입의 동일성 비교에 필요)
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Address address = (Address) o;

        return city.equals(address.city) && street.equals(address.street) && zipcode.equals(address.zipcode);
    }

    @Override
    public int hashCode() {
        return Objects.hash(city, street, zipcode);
    }
}

2. 컬렉션 값 타입을 사용하는 엔티티

import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;

@Entity
public class Member {

    @Id @GeneratedValue
    private Long id;

    private String name;

    // 컬렉션 값 타입 사용
    @ElementCollection
    @CollectionTable(name = "member_address", joinColumns = @JoinColumn(name = "member_id"))
    private List<Address> addresses = new ArrayList<>();

    // 기본 생성자, 게터, 세터
    public Member() {}

    public Member(String name) {
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public List<Address> getAddresses() {
        return addresses;
    }

    public void addAddress(Address address) {
        addresses.add(address);
    }
}

설명

  1. @Embeddable:
    • Address 클래스는 값 타입을 나타내며 @Embeddable로 정의되었습니다. 값 타입은 기본적으로 엔티티가 아니므로, 별도의 테이블 없이 부모 엔티티의 테이블에 포함되거나, 컬렉션 값 타입처럼 별도의 테이블에 저장될 수 있습니다.
  2. @ElementCollection:
    • @ElementCollection은 엔티티가 아닌 값 타입 컬렉션을 관리할 때 사용합니다. List, Set 등의 컬렉션을 값 타입으로 저장합니다.
  3. @CollectionTable:
    • @CollectionTable은 값 타입 컬렉션을 저장할 별도의 테이블을 지정하는데 사용됩니다. 여기서 member_address 테이블이 생성되고, member_id 컬럼을 통해 Member 엔티티와 연결됩니다.
  4. 컬렉션 값 타입 관리:
    • Member 엔티티는 여러 Address 값을 가질 수 있습니다. 예를 들어, 한 사용자가 여러 주소를 가질 때 List<Address> 컬렉션으로 관리할 수 있습니다.

컬렉션 값 타입의 제약사항

  • 값 타입 수정 시 삭제 후 재삽입: JPA는 값 타입 컬렉션을 수정할 때, 기존 데이터를 삭제하고 새로 삽입하는 방식으로 처리합니다. 이는 데이터베이스 상에서 값 타입 컬렉션의 변경을 처리하는 일반적인 방식입니다.
  • 영속성 전이 및 고아 객체 처리: 컬렉션 값 타입은 엔티티의 생명주기에 종속적이므로, 엔티티가 삭제되면 값 타입 컬렉션도 함께 삭제됩니다.

요약

  • 컬렉션 값 타입은 JPA에서 값 타입을 컬렉션으로 관리하는 기능으로, @ElementCollection@Embeddable을 사용해 구현됩니다.
  • 컬렉션 값 타입은 데이터베이스에 별도의 테이블에 저장되며, 엔티티가 관리하는 값 타입의 모음을 표현합니다.
  • 컬렉션 값 타입은 엔티티 생명주기에 종속적이고, 불변 객체로 사용하는 것이 권장됩니다.

CROSS JOIN

 

CROSS JOIN은 SQL에서 사용되는 조인 방식 중 하나로, 두 테이블의 모든 행을 조합하는 연산입니다. 카테시안 곱(Cartesian Product)이라고도 불리며, 두 테이블 간에 모든 가능한 행의 조합을 반환합니다. 즉, 첫 번째 테이블의 각 행과 두 번째 테이블의 모든 행을 매칭하여 결과를 반환합니다.

CROSS JOIN의 특징

  1. 카테시안 곱:
    • 두 테이블의 모든 조합을 생성합니다. 예를 들어, 첫 번째 테이블에 5개의 행, 두 번째 테이블에 3개의 행이 있다면, 결과는 5 * 3 = 15개의 행이 됩니다.
  2. 결합 조건이 없음:
    • CROSS JOIN은 두 테이블 간의 결합 조건을 사용하지 않습니다. 그래서 두 테이블의 모든 행을 단순히 조합합니다.
  3. 모든 행의 조합:
    • CROSS JOIN은 두 테이블 간의 모든 가능한 행의 조합을 반환하므로, 필요 이상으로 많은 결과가 나올 수 있어 주의가 필요합니다.

CROSS JOIN의 예시

예시 테이블

  1. Customers 테이블:
customer_id customer_name
1 John
2 Jane
  1. Products 테이블:
product_id product_name
101 Laptop
102 Phone
103 Tablet

CROSS JOIN 쿼리

SELECT *
FROM Customers
CROSS JOIN Products;

결과:

customer_id customer_name product_id product_name
1 John 101 Laptop
1 John 102 Phone
1 John 103 Tablet
2 Jane 101 Laptop
2 Jane 102 Phone
2 Jane 103 Tablet

이 결과는 Customers 테이블의 각 고객이 Products 테이블의 모든 제품과 조합된 결과를 보여줍니다. 두 테이블의 행들이 조합되어 총 6개의 행이 반환됩니다.

주의사항

  • 대용량 테이블에서는 CROSS JOIN을 사용할 때 주의해야 합니다. 테이블의 행이 많을 경우, 결과는 기하급수적으로 증가하여 성능에 큰 영향을 미칠 수 있습니다.
  • 의도적으로 사용: CROSS JOIN은 보통 특정 의도를 가지고 사용할 때만 적용하는 것이 좋습니다. 두 테이블의 모든 조합을 생성하고자 하는 특별한 경우가 아니라면, 조건이 없는 CROSS JOIN은 사용하지 않는 것이 좋습니다.

요약

  • CROSS JOIN두 테이블의 모든 행을 조합하여 결과를 반환합니다.
  • 테이블 간의 모든 가능한 조합을 반환하므로, 결과의 행 수가 매우 많아질 수 있습니다.
  • 카테시안 곱을 수행하며, 결합 조건이 없기 때문에 두 테이블의 모든 행이 매칭됩니다.