728x90
반응형
SMALL

2024.10.31 업데이트


참고로, 이 포스팅에서 가장 중요한 부분은 거의 마지막에 나온다!

 

 

 

엔티티에 컬렉션으로 관리하는 데이터는 흔히 있을 수 있는 일이지만 DB는 기본적으로는 컬렉션 데이터를 지원하지 않는다. 물론 요즘은 여러 방법으로 컬렉션을 테이블에서 관리할 수 있지만(JSON으로 데이터를 저장한다든지 등) 그러나 정석적인 방법은 컬렉션 데이터를 테이블로 만들어 참조로 관리하는 이다. 

 

반응형
SMALL

아래 그림을 보자. 

멤버라는 엔티티가 관리하는 데이터 favoriteFoodsaddressHistory는 컬렉션 값 타입이다. 이런 엔티티를 테이블화 하기 위해서는 각 컬렉션 값 타입을 테이블로 분리해서 1:N 관계로 만드는 것이다. 이게 정석적인 방법이다. 

 

위 그림을 보면 ADDRESSFAVORITE_FOOD도 모든 필드가 하나의 PK인데 이 이유는 컬렉션 '값 타입'이기 때문이다. 값 타입은 하나의 레코드 자체가 고유값이 되어야 하는 것이다. 만약, 여기서 어떤 구분할 수 있는 PK가 따로 있으면 그건 값 타입이 아니라 엔티티라고 불려야한다. 구현하는 방법도 간단하다. 코드를 보자.

 

Address

package cwchoiit.embedded;

import javax.persistence.Embeddable;
import java.util.Objects;

@Embeddable
public class Address {
    private final String city;
    private final String street;
    private final String zipCode;

    public Address() {
        this.city = null;
        this.street = null;
        this.zipCode = null;
    }

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

    public String getCity() {
        return city;
    }

    public String getStreet() {
        return street;
    }

    public String getZipCode() {
        return zipCode;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Address address = (Address) o;
        return Objects.equals(city, address.city) && Objects.equals(street, address.street) && Objects.equals(zipCode, address.zipCode);
    }

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

 

EmbedMember

package cwchoiit.embedded;

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

@Entity
public class EmbedMember {

    @Id
    @GeneratedValue
    private Long id;

    private String username;

    @Embedded
    private Period workPeriod;

    @Embedded
    private Address homeAddress;

    @Embedded
    @AttributeOverrides({
            @AttributeOverride(name="city", column=@Column(name = "OFFICE_CITY")),
            @AttributeOverride(name="street", column=@Column(name = "OFFICE_STREET")),
            @AttributeOverride(name="zipCode", column=@Column(name = "OFFICE_ZIPCODE"))
    })
    private Address officeAddress;

    @ElementCollection
    @CollectionTable(name = "FAVORITE_FOOD", joinColumns = @JoinColumn(name = "MEMBER_ID"))
    @Column(name = "FAV_FOOD")
    private Set<String> favoriteFoods = new HashSet<>();

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

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Period getWorkPeriod() {
        return workPeriod;
    }

    public void setWorkPeriod(Period workPeriod) {
        this.workPeriod = workPeriod;
    }

    public Address getHomeAddress() {
        return homeAddress;
    }

    public void setHomeAddress(Address homeAddress) {
        this.homeAddress = homeAddress;
    }

    public Address getOfficeAddress() {
        return officeAddress;
    }

    public void setOfficeAddress(Address officeAddress) {
        this.officeAddress = officeAddress;
    }

    public Set<String> getFavoriteFoods() {
        return favoriteFoods;
    }

    public void setFavoriteFoods(Set<String> favoriteFoods) {
        this.favoriteFoods = favoriteFoods;
    }

    public List<Address> getAddressHistory() {
        return addressHistory;
    }

    public void setAddressHistory(List<Address> addressHistory) {
        this.addressHistory = addressHistory;
    }
}
  • 여기서 주의깊게 볼 부분은 바로 이 부분이다.
@ElementCollection
@CollectionTable(name = "FAVORITE_FOOD", joinColumns = @JoinColumn(name = "MEMBER_ID"))
@Column(name = "FAV_FOOD")
private Set<String> favoriteFoods = new HashSet<>();

@ElementCollection
@CollectionTable(name = "ADDRESS", joinColumns = @JoinColumn(name = "MEMBER_ID"))
private List<Address> addressHistory = new ArrayList<>();
  • 멤버 엔티티가 관리하는 컬렉션 두 개가 있다. favoriteFoods, addressHistory.
  • 이런 컬렉션 형태의 필드를 테이블화하기 위해 두 가지의 어노테이션이 필요하다. @ElementCollection, @CollectionTable. @CollectionTable 어노테이션에 name property는 테이블명을 의미한다. joinColumns는 이 테이블이 어떤 테이블과 조인될지를 선정한다. 즉, 외래키를 받는 부분이고 멤버에 속한 favoriteFoods이고 addressHistory니까 MEMBER_ID라는 외래키를 적용한다. 뭐 M_ID, MID라고 해도 상관은 없다.
  • favoriteFoods같은 경우엔 컬렉션에 들어있는 값이 String으로 된 문자열 단일 값이기 때문에 컬럼명을 지정해주기 위해 @Column 어노테이션도 사용했다. Address{city, street, zipCode} 같은 경우는 세 개의 필드가 한 객체로 만들어져 있으니 이런게 불가능하다. 사용하는게 필수는 아닌데 이런 경우에 이렇게 컬럼명도 지정할 수 있단 사실! 이렇게 두 개의 어노테이션만 있으면 자동으로 컬렉션 값 타입은 테이블로써 만들어지게 된다.

 

실행해보자.

Main

package cwchoiit;

import cwchoiit.embedded.Address;
import cwchoiit.embedded.EmbedMember;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import java.util.List;
import java.util.Set;

public class JpaMain {
    public static void main(String[] args) {
        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("hellojpa");
        EntityManager entityManager = entityManagerFactory.createEntityManager();

        entityManager.getTransaction().begin();
        try {
            EmbedMember member = new EmbedMember();
            member.setUsername("member1");
            member.setHomeAddress(new Address("city", "street", "zipcode"));

            member.getFavoriteFoods().add("치킨");
            member.getFavoriteFoods().add("피자");
            member.getFavoriteFoods().add("족발");

            member.getAddressHistory().add(new Address("city1", "street1", "zipCode1"));
            member.getAddressHistory().add(new Address("city2", "street2", "zipCode2"));

            entityManager.persist(member);

            entityManager.getTransaction().commit();
        } catch (Exception e) {
            entityManager.getTransaction().rollback();
        } finally {
            entityManager.close();
        }
        entityManagerFactory.close();
    }
}
  • 코드를 보면 멤버 객체를 하나 생성하고 멤버 객체의 favoriteFoodsaddressHistory를 가져와 추가했다. 저 두 개의 엔티티는 멤버라는 부모 엔티티에 의해 생명주기가 관리된다. 왜냐? 값 타입이기 때문이다. 즉, 멤버 객체가 생성되고 소멸되는 주기가 곧 저 두 엔티티의 생명주기이고 멤버에 의해 관리 되기 때문에 영속 컨텍스트에는 멤버만을 추가해도 알아서 새로이 추가된 favoriteFoodsaddressHistoryINSERT문으로 나간다.

결과를 보자.

Hibernate: 
    insert 
    into
        Member
        (city, street, zipcode, name, MEMBER_ID) 
    values
        (?, ?, ?, ?, ?)
Hibernate: 
    insert 
    into
        ADDRESS_HISTORY
        (MEMBER_ID, city, street, zipcode) 
    values
        (?, ?, ?, ?)
Hibernate: 
    insert 
    into
        FAVORITE_FOOD
        (MEMBER_ID, FOOD_NAME) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        FAVORITE_FOOD
        (MEMBER_ID, FOOD_NAME) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        FAVORITE_FOOD
        (MEMBER_ID, FOOD_NAME) 
    values
        (?, ?)

  • Member, AddressHistory, FavoriteFood 모두 INSERT문이 실행됐음을 확인할 수 있다. 컬렉션 타입은 이렇게 테이블화해서 관계 매핑으로 다루어야 한다.

그리고 이렇게 잘 들어갔다면, 한번 멤버를 조회해 보자. 어떻게 나올까?

값 타입 컬렉션 조회하기

package cwchoiit;

import cwchoiit.embedded.Address;
import cwchoiit.embedded.EmbedMember;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import java.util.List;
import java.util.Set;

public class JpaMain {
    public static void main(String[] args) {
        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("hellojpa");
        EntityManager entityManager = entityManagerFactory.createEntityManager();

        entityManager.getTransaction().begin();
        try {
            EmbedMember member = new EmbedMember();
            member.setUsername("member1");
            member.setHomeAddress(new Address("city", "street", "zipcode"));

            member.getFavoriteFoods().add("치킨");
            member.getFavoriteFoods().add("피자");
            member.getFavoriteFoods().add("족발");

            member.getAddressHistory().add(new Address("city1", "street1", "zipCode1"));
            member.getAddressHistory().add(new Address("city2", "street2", "zipCode2"));

            entityManager.persist(member);

            entityManager.flush();
            entityManager.clear();

            System.out.println("================================================================");
            EmbedMember findMember = entityManager.find(EmbedMember.class, member.getId());

            entityManager.getTransaction().commit();
        } catch (Exception e) {
            entityManager.getTransaction().rollback();
        } finally {
            entityManager.close();
        }
        entityManagerFactory.close();
    }
}

실행 결과

  • 실행 결과를 보면, 멤버 정보만 가져왔지 favoriteFoods, addressHistory 정보는 가져오지 않았다. 즉, 지연 로딩이라는 얘기다. 이렇게 값 타입 컬렉션은 기본이 지연로딩이다. 아래 사진을 보면 알 수 있다.

 

그래서, 꺼내와서 메서드를 호출해야 실제로 쿼리가 나가서 값을 가져온다.

package cwchoiit;

import cwchoiit.embedded.Address;
import cwchoiit.embedded.EmbedMember;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import java.util.List;
import java.util.Set;

public class JpaMain {
    public static void main(String[] args) {
        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("hellojpa");
        EntityManager entityManager = entityManagerFactory.createEntityManager();

        entityManager.getTransaction().begin();
        try {
            EmbedMember member = new EmbedMember();
            member.setUsername("member1");
            member.setHomeAddress(new Address("city", "street", "zipcode"));

            member.getFavoriteFoods().add("치킨");
            member.getFavoriteFoods().add("피자");
            member.getFavoriteFoods().add("족발");

            member.getAddressHistory().add(new Address("city1", "street1", "zipCode1"));
            member.getAddressHistory().add(new Address("city2", "street2", "zipCode2"));

            entityManager.persist(member);

            entityManager.flush();
            entityManager.clear();

            System.out.println("================================================================");
            EmbedMember findMember = entityManager.find(EmbedMember.class, member.getId());

            List<Address> addressHistory = findMember.getAddressHistory();
            for (Address address : addressHistory) {
                System.out.println("address = " + address.getCity());
            }

            Set<String> favoriteFoods = findMember.getFavoriteFoods();
            for (String favoriteFood : favoriteFoods) {
                System.out.println("favoriteFood = " + favoriteFood);
            }
            
            entityManager.getTransaction().commit();
        } catch (Exception e) {
            entityManager.getTransaction().rollback();
        } finally {
            entityManager.close();
        }
        entityManagerFactory.close();
    }
}

실행 결과

 

 

근데 만약 데이터를 수정해야할 때는 어떻게 동작할까? 아래 수정 코드를 보자.

값 타입 컬렉션 수정하기

package cwchoiit;

import cwchoiit.embedded.Address;
import cwchoiit.embedded.EmbedMember;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import java.util.List;
import java.util.Set;

public class JpaMain {
    public static void main(String[] args) {
        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("hellojpa");
        EntityManager entityManager = entityManagerFactory.createEntityManager();

        entityManager.getTransaction().begin();
        try {
            EmbedMember member = new EmbedMember();
            member.setUsername("member1");
            member.setHomeAddress(new Address("city", "street", "zipcode"));

            member.getFavoriteFoods().add("치킨");
            member.getFavoriteFoods().add("피자");
            member.getFavoriteFoods().add("족발");

            member.getAddressHistory().add(new Address("city1", "street1", "zipCode1"));
            member.getAddressHistory().add(new Address("city2", "street2", "zipCode2"));

            entityManager.persist(member);

            entityManager.flush();
            entityManager.clear();

            System.out.println("================================================================");
            EmbedMember findMember = entityManager.find(EmbedMember.class, member.getId());

            List<Address> addressHistory = findMember.getAddressHistory();
            for (Address address : addressHistory) {
                System.out.println("address = " + address.getCity());
            }

            Set<String> favoriteFoods = findMember.getFavoriteFoods();
            for (String favoriteFood : favoriteFoods) {
                System.out.println("favoriteFood = " + favoriteFood);
            }

            // 컬렉션 값 타입의 내용을 수정하는 방법
            favoriteFoods.remove("치킨");
            favoriteFoods.add("소고기");

            // 컬렉션 값 타입의 내용을 수정하는 방법 (remove 하는건 똑같다, 근데 여기는 레퍼런스를 받기 때문에 반드시 equals 를 구현해 놓은 상태여야 한다)
            addressHistory.remove(new Address("city1", "street1", "zipCode1"));
            addressHistory.add(new Address("city3", "street3", "zipCode3"));

            entityManager.getTransaction().commit();
        } catch (Exception e) {
            entityManager.getTransaction().rollback();
        } finally {
            entityManager.close();
        }
        entityManagerFactory.close();
    }
}
  • 새 멤버를 만들 때 addressHistory, favoriteFoods를 추가한 후 멤버만을 persist()해도 flush()를 호출하면 다음과 같이 데이터베이스에 Member, addressHistory, favoriteFoods가 모두 추가된다. 그 이유는 값 타입은 어떤것이든 엔티티의 생명주기에 종속적이기 때문이다. 
  • 이 말은, 멤버의 favoriteFoods를 가져와 요소를 날리면? favoriteFoods의 해당 레코드는 삭제가 된다. 반대로 멤버의 favoriteFoods를 가져와 요소를 추가하면? favoriteFoods에 추가한 요소가 레코드로 저장된다. 그리고 favoriteFoods는 컬렉션에 들어가는 타입이 String이라서 remove() 호출 시에 동등 비교를 위해 따로 해줄 일이 없다. 기본으로 equals()가 잘 구현되어 있기 때문이다.
  • 그러나, addressHistory 같은 경우, 컬렉션에 들어가는 타입이 Address라는 우리가 직접 만든 객체이므로, 리스트에서 이 요소를 제거하려면 equals()가 반드시 구현되어 있어야 한다. 그래야 remove() 호출 시 내부에서 equals()를 호출해서 동일한 요소를 찾아내 지울 수 있기 때문이다.
  • 그래서 위에 Address 코드를 보면, equals() hashCode()를 구현한 모습을 볼 수 있다.

 

그럼 실행해보자. 어떤 쿼리가 나갈까? 

  • 아래 파란 박스는 favoriteFoods 관련 쿼리이고, 위에 빨간 박스는 addressHistory 관련 쿼리이다.
  • favoriteFoods는 보다시피, 원하는 값을 하나 삭제하고 원하는 값을 하나 추가했다. 아주 마음에 든다.
  • 그러나 문제는 addressHistory이다. 얘를 보면 지금, 세개의 쿼리가 나갔는데 처음 쿼리가 전체를 삭제하는 쿼리다! 그리고 원래 있던 레코드를 하나 추가하고, 새로 추가하려고 했던 레코드를 추가한 것이다. 
  • 전체를 삭제해버린 후 기존에 남은 하나와 새로 추가한 하나를 나란히 INSERT한다. 이것이 값 타입 컬렉션을 테이블로 관리할 때 가장 큰 문제가 된다. 값 타입 컬렉션 테이블은 식별자가 따로 없기 때문에 JPA가 삭제할 때 어떤 데이터를 가져와 삭제해야 하는지 알지 못한다. 그렇기 때문에 전체를 삭제한 후 종속된 엔티티가 가지고 있는 모든 데이터를 다시 하나씩 추가한다. 
  • 근데 Set은 하나만 딱 잘 골라서 삭제하고 원하는거 추가하는데 List는 왜 안돼요? → SetList의 차이 그대로이기 때문이다. Set은? 중복값이란게 존재할 수가 없는 자료구조이다. 그렇기 때문에 내가 원하는 것을 삭제한다고 하면 JPA도 그 값을 그대로 찾아서 삭제하면 된다. List는? 중복값이란게 존재할 수 있는 자료구조다. 그 말은 내가 삭제하고자 하는 데이터가 한개뿐이 아니라 두개 세개가 있을 수도 있는데 이걸 JPA는 알지 못하기 때문에 다 날리고 새로 넣을 것들을 다시 넣게 되는 것이다.

🟠 결론은, 이 값 타입 컬렉션을 쓰면 안된다. 🟠

 

값 타입 컬렉션을 엔티티로 변경하라

값 타입 컬렉션의 가장 큰 문제는 식별자가 없기에 수정 쿼리를 수행할 때 JPA가 찾아내지 못한다는 것이다. 그리고 반대로 말하면 식별자가 있는 테이블은 엔티티라고 표현해야 한다. 그럼 위 코드의 문제점을 어떻게 수정하면 될까?

 

AddressEntity라는 엔티티를 만들고 기존의 값 타입 컬렉션을 엔티티로 승격시켜 식별자를 가지게 하는것이다.

 

AddressEntity

package cwchoiit.embedded;

import javax.persistence.*;

@Entity
public class AddressEntity {
    @Id
    @GeneratedValue
    private Long id;

    private Address address;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "MEMBER_ID")
    private EmbedMember member;

    public AddressEntity() {
    }

    public AddressEntity(Address address) {
        this.address = address;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public EmbedMember getMember() {
        return member;
    }

    public void setMember(EmbedMember member) {
        this.member = member;
    }
}

 

 

package cwchoiit.embedded;

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

@Entity
public class EmbedMember {

    @Id
    @GeneratedValue
    private Long id;

    private String username;

    @Embedded
    private Period workPeriod;

    @Embedded
    private Address homeAddress;

    @Embedded
    @AttributeOverrides({
            @AttributeOverride(name="city", column=@Column(name = "OFFICE_CITY")),
            @AttributeOverride(name="street", column=@Column(name = "OFFICE_STREET")),
            @AttributeOverride(name="zipCode", column=@Column(name = "OFFICE_ZIPCODE"))
    })
    private Address officeAddress;

    @ElementCollection
    @CollectionTable(name = "FAVORITE_FOOD", joinColumns = @JoinColumn(name = "MEMBER_ID"))
    @Column(name = "FAV_FOOD")
    private Set<String> favoriteFoods = new HashSet<>();

    @OneToMany(mappedBy = "member")
    private List<AddressEntity> addressHistory = new ArrayList<>();

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Period getWorkPeriod() {
        return workPeriod;
    }

    public void setWorkPeriod(Period workPeriod) {
        this.workPeriod = workPeriod;
    }

    public Address getHomeAddress() {
        return homeAddress;
    }

    public void setHomeAddress(Address homeAddress) {
        this.homeAddress = homeAddress;
    }

    public Address getOfficeAddress() {
        return officeAddress;
    }

    public void setOfficeAddress(Address officeAddress) {
        this.officeAddress = officeAddress;
    }

    public Set<String> getFavoriteFoods() {
        return favoriteFoods;
    }

    public void setFavoriteFoods(Set<String> favoriteFoods) {
        this.favoriteFoods = favoriteFoods;
    }

    public List<AddressEntity> getAddressHistory() {
        return addressHistory;
    }

    public void setAddressHistory(List<AddressEntity> addressHistory) {
        this.addressHistory = addressHistory;
    }
}
  • 여기서는 다대일 양방향으로 매핑했다. 여기서 누군가는 "그냥 Member가 외래키 관리(일대다 방식)하고 CRUD 편리하게 리스트로 하면 안되나요? 일대다로 하면 안되나요?" 그렇게 해도 된다! 이 경우에는 AddressEntity가 멤버에 종속된 느낌이 강하게 들기 때문에 그렇게 만들어도 될 것 같다.
  • 그러나, 그렇게 만들면 이제 알아두어야 할 점은 결국 데이터베이스 테이블 관점에서는 Member에 외래키가 있는게 아니고 AddressEntity에 외래키가 있다는 점은 인지해야 하고, 그렇기 때문에, MemberAddressHistory를 가져와서 값을 추가하고 빼고 해도 나가는 쿼리는 AddressEntity의 업데이트 쿼리라는 것을 인지하고 있으면 된다.

 

이제 실행해 보자. 실행 코드는 다음과 같다.

package cwchoiit;

import cwchoiit.embedded.Address;
import cwchoiit.embedded.AddressEntity;
import cwchoiit.embedded.EmbedMember;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import java.util.List;
import java.util.Set;

public class JpaMain {
    public static void main(String[] args) {
        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("hellojpa");
        EntityManager entityManager = entityManagerFactory.createEntityManager();

        entityManager.getTransaction().begin();
        try {
            EmbedMember member = new EmbedMember();
            member.setUsername("member1");
            member.setHomeAddress(new Address("city", "street", "zipcode"));

            member.getFavoriteFoods().add("치킨");
            member.getFavoriteFoods().add("피자");
            member.getFavoriteFoods().add("족발");

            AddressEntity addressEntity = new AddressEntity();
            addressEntity.setAddress(new Address("city1", "street1", "zipCode1"));
            addressEntity.setMember(member);

            AddressEntity addressEntity2 = new AddressEntity();
            addressEntity2.setAddress(new Address("city2", "street2", "zipCode2"));
            addressEntity2.setMember(member);

            entityManager.persist(addressEntity);
            entityManager.persist(addressEntity2);
            entityManager.persist(member);

            entityManager.flush();
            entityManager.clear();

            System.out.println("================================================================");

            // 값 타입 컬렉션을 엔티티로 승격시키고, 수정하는 방법
            AddressEntity ae = entityManager.find(AddressEntity.class, 1L);
            ae.setAddress(new Address("newCity", "newStreet", "newZipCode"));

            entityManager.getTransaction().commit();
        } catch (Exception e) {
            entityManager.getTransaction().rollback();
        } finally {
            entityManager.close();
        }
        entityManagerFactory.close();
    }
}

 

실행 결과

  • 더 이상 전체 Address 정보를 삭제하지 않고, 삭제하고 추가하고자 하는 하나의 AddressEntity 레코드만을 업데이트하는 모습을 확인할 수 있다. 

 

결론 

값 타입은 정말 값 타입이라 판단될 때만 사용해야 한다. 즉, 식별자가 필요하지 않고 엔티티에 종속된 생명 주기를 가져야 하는 경우에만 사용해야 하고 식별자가 필요하고 본인만의 생명 주기가 필요한 경우 엔티티로 승격시키자! 

 

그리고 사실 실무에서는 값 타입이 그렇게 많이 사용되지도 않는다. Position{x, y} 이렇게 x좌표, y좌표를 한번에 묶어서 가지는 값같은 정말 값 타입이면 되는 그런 경우 말고는 많이 사용되지 않는다.

728x90
반응형
LIST

+ Recent posts