값 타입 - 컬렉션3

PPakSSam·2022년 1월 19일
0
post-thumbnail

값 타입 - 컬렉션 순서


컬렉션 값 타입 대안

  • 실무에서는 상황에 따라(대부분) 값 타입 컬렉션 대신에 일대다 관계를 고려한다.
  • 일대다 관계를 위한 엔티티를 만들고, 여기에서 값 타입을 사용한다.
  • 영속성 전이(Cascade) + 고아 객체 제거를 사용해서 값 타입 컬렉션 처럼 사용한다.

[AddressEntity]

@Entity
@Table(name = "ADDRESS")
public class AddressEntity {
    
    @Id @GeneratedValue
    private Long id;
    
    @Embedded
    private Address address; // 값 타입 사용
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        AddressEntity that = (AddressEntity) o;
        return  Objects.equals(address, that.address);
    }

    @Override
    public int hashCode() {
        return Objects.hash(address);
    }
}  

[Member]

@Entity
public class Member {
    
    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;
    
    @Column(name="USERNAME")
    private String username;
    
    @Embedded
    private Address homeAddress;
    
    // 컬렉션 값 타입 대신에 일대다 매핑
    @OneToMany(cascade = CascadeType.ALL, orpahnRemoval = true)
    @JoinColumn(name = "MEMBER_ID")
    private List<AddressEntity> addressHistory = new ArrayList<>();
}

[JpaMain]

Member member = new Member();
member.setUsername("member");
member.setHomeAddress(new Address("homeCity", "street", "10000"));

member.getAddressHistory().add(new AddressEntity("old1", "street", "10000"));
member.getAddressHistory().add(new AddressEntity("old2", "street", "10000"));

em.persist(member);

em.flush();
em.clear();

Member findMember = em.find(Member.class, member.getId());

// 히스토리 바꾸기
findMember.getAddressHistory().remove(new AddressEntity("old1", "street", "10000"));
findMember.getAddressHistory().add(new AddressEntity("newCity1", "street", "10000"));


참고

위의 예시는 일대다 단방향 매핑이다.
따라서 JpaMain의 코드를 실행시키면 다음과 같이 쿼리가 호출된다.

insert 
    into
        member
        (city, street, zipcode, username, member_id) 
    values
        (?, ?, ?, ?, ?)

// insert 쿼리 2번 -> address가 2개 있으므로
insert 
    into
        address
        (city, street, zipcode, id) 
    values
        (?, ?, ?, ?)
        
// update 쿼리 2번 -> address가 2개 있으므로
update
        address 
    set
        member_id=? 
    where
        id=?

양방향보다 단방향이 좋다? 에서 일대다 단방향 매핑의 경우 update 쿼리가 나갈 수 있으므로, 다대일 양방향 매핑을 추천한다는 글을 쓴 적이 있다.

따라서 위의 예시를 다대일 양방향 매핑으로 바꾼 후 진행해 봤다.

[Member]

@Getter @Setter
@Entity
public class Member {

    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    @Column(name = "USERNAME")
    private String username;

    @Embedded
    private Address homeAddress;

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "member")
    private List<AddressEntity> addressHistory = new ArrayList<>();
}

[AddressEntity]

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "ADDRESS")
@Entity
public class AddressEntity {

    @Id @GeneratedValue
    private Long id;

    @Embedded
    private Address address;

    @ManyToOne
    @JoinColumn(name = "MEMBER_ID")
    private Member member;

    public AddressEntity(String city, String street, String zipcode, Member member) {
        this.address = new Address(city, street, zipcode);
        this.member = member;
    }
}

[TestCode]

Member member = new Member();
member.setUsername("member1");
member.setHomeAddress(new Address("homeCity", "street", "10000"));

member.getAddressHistory().add(new AddressEntity("old1", "street", "10000", member));
member.getAddressHistory().add(new AddressEntity("old1", "street", "10000", member));

memberRepository.save(member);
insert 
    into
        member
        (city, street, zipcode, username, member_id) 
    values
        (?, ?, ?, ?, ?)

// insert 쿼리 2번 
insert 
    into
        address
        (city, street, zipcode, member_id, id) 
    values
        (?, ?, ?, ?, ?)

이렇게 다대일 양방향 매핑으로 변환 후 실행하면 update 쿼리가 나가지 않음을 알 수 있다.

profile
성장에 대한 경험을 공유하고픈 자발적 경험주의자

0개의 댓글