본문 바로가기

스터디/JPA

값 타입 - (2)

728x90
반응형

값 타입과 불변 객체


값 타입 공유 참조

값 타입을 여러 엔티티에 공유하면 위험하다

 

예로 들어, 회원1,2가 같은 값 타입을 공유할 때, 회원1의 값을 바꾸면 회원2의 값도 함께 바뀐다

 

이런 현상을 부작용이라 한다

값 타입 복사

값을 복사해서 사용하면 공유 참조로 인해 발생하는 부작용을 피할 수 있다

 

자바는 기본 타입에 값을 대입(=)하면 값을 복사해서 전달한다

 

자바는 객체에 값을 대입하면 항상 참조 값을 전달한다

복사하지 않고 원본의 참조 값을 직접 넘기는 것을 막을 방법이 없다

불변 객체

객체를 불변하게 만들면 값을 수정할 수 없으므로 부작용을 차단할 수 있으므로 값 타입은 가능하다면 불변 객체로 설계해야 한다

 

불변 객체수정자를 만들지 않는 형태로 구현되며, 값을 수정해야 할 경우 새로운 객체를 생성해 사용해야 한다

값 타입의 비교


 

동일성 비교: 인스턴스의 참조 값을 비교

동등성 비교: 인스턴스의 값을 비교

 

인스턴스가 달라도 값이 같으면 같은 것으로 봐야 한다

equals() 메소드를 재정의해 동등성 비교를 사용해야 한다

값 타입 컬렉션


 

값 타입을 하나 이상 저장하려면 컬렉션에 보관하고 @ElementCollection, @CollectionTable을 사용하면 된다

@Entity
public class Member {
	
	@Id @GeneratedValue
	private Long id;
	
	@Embedded
	private Address homeAddress;
	
	@ElementCollection
	@CollectionTable(name="FAVORIATE_FOODS",
		joinColumns=@JoinColumn(name="MEMBER_ID")
	@Column(name="FOOD_NAME")
	private Set<String> favoriteFoods = new HashSet<String>();
	
	@ElementCollection
	@CollectionTable(name="ADDRESS",
		joinColumns=@JoinColumn(name="MEMBER_ID")
	private List<Address> addressHistory = new ArrayList<Address>();
	...
}

@Embeddable
public class Address {

	@Column
	private String city;
	private String street;
	private String zipcode;
	...
}

 

데이터베이스 테이블은 컬럼 안에 컬렉션을 포함할 수 없다

값 타입 컬렉션을 저장한 별도의 테이블을 추가하고 추가한 테이블을 매핑해야 한다(@CollectionTable)

 

추가한 테이블의 값으로 사용되는 컬럼이 하나 뿐이라면 @Column을 통해 컬럼명을 지정할 수 있다

 

@CollectoinTable을 생략하면 기본 값을 사용해서 매핑한다 (엔티티이름_컬렉션 속성이름)

값 타입 컬렉션 사용

Member member = new Member();

member.setHomeAddress(new Address("통영", "몽돌해수욕장", "660-123");

member.getFavoriteFoods().add("짬뽕");
member.getFavoriteFoods().add("짜장면");
member.getFavoriteFoods().add("탕수육");

member.getAddressHistory().add(new Address("서울", "강남", "123-123"));
member.getAddressHistory().add(new Address("서울", "강북", "000-000"));

em.persist(member);

마지막에 엔티티를 영속화하면, 해당 엔티티의 값 타입도 함께 저장한다

영속화 과정에서 INSERT문은 총 6번 실행된다

  - INSERT문: member 1번, favorite_food 3번, address_history 2번 → 총 6번

 

값 타입 컬렉션 조회 시 페치 전략을 선택할 수 있는데, 기본은 LAZY이다

  - @ElementCollection(fetch=FetchType.LAZY)

 

Member member = em.find(Member.class, 1L);

member.setHomeAddress(new Address("새로운도시", "신도시1", "123456");

Set<String> favoriteFoods = member.getFavoriteFoods();
favoriteFoods.remove("탕수육");
favoriteFoods.add("치킨")

List<Address> addressHistory = member.getAddressHistory();
addressHistory.remove(new Address("서울", "기존 주소", "123-123");
addressHistory.add(new Address("새로운도시", "새로운 주소", "123-456");

임베디드 값 타입은 수정할 새로운 객체를 생성해 해당 값을 수정하면 된다

 

값 타입 컬렉션의 경우 기존 값 타입을 수정하는 것이 아니라 값 타입을 삭제하고 새로운 값 타입을 추가해야 한다

  - 값 타입은 equals, hashcode를 꼭 구현해야 한다

값 타입 컬렉션의 제약사항

1. 값 타입은 식별자가 없는 단순한 값의 모음이므로 값을 변경하면 데이터베이스에 저장된 원본 데이터를 찾기 어렵다

 -> 값 타입은 변경되어도 자신이 소속된 엔티티를 찾아 변경하면 되지만, 값 타입 컬렉션은 값 타입들이 별도의 테이블에 저장되는데 여기에 보관된 값 타입이 변경되면 원본 데이터를 찾기 어렵다

 

2. 값 타입 컬렉션에 변경 사항이 발생하면 값 타입 컬렉션의 모든 데이터를 삭제하고 모든 데이터를 다시 저장한다

-> 값 타입 컬렉션이 매핑된 테이블의 데이터가 많다면 값 타입 컬렉션 대신 일대다 관계를 고려해야 한다

 

3. 값 타입 컬렉션을 매핑하는 테이블은 모든 컬럼을 묶어서 기본키를 구성한다

-> 기본 키 제약 조건으로 인해 컬럼에 null을 입력할 수 없다

-> 같은 값을 중복해서 저장할 수 없다

 

일대다 관계 + 영속성 전이 + 고아 객체 제거 기능을 사용하면 값 타입 컬렉션처럼 사용할 수 있다

728x90
반응형

'스터디 > JPA' 카테고리의 다른 글

객체지향 쿼리 언어 - (2)  (0) 2025.02.17
객체지향 쿼리 언어 - (1)  (0) 2025.02.16
값 타입 - (1)  (0) 2025.02.14
프록시와 연관관계 정리 - (3)  (1) 2025.02.14
프록시와 연관관계 정리 - (2)  (0) 2025.02.13