Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
Tags
- TABLE
- 양방향 매핑
- ddl-auto
- 기본값 타입
- 세션 생성 정책
- 임베디드 타입
- Remember-me
- Spring Security
- field column mapping
- 지연 로딩
- 기본 키 매핑
- 값 타입 컬렉션
- 세션 고정 보호
- AnonymousAuthenticationFilter
- 동시 세션 제어
- @MappedSuperclasss
- MySQL
- JPQL
- spring jpa
- 즉시 로딩
- SessionManagementFilter
- concurrentsessionfilter
- jpa
- form login
- 다대다
- Database
- 값 타입 비교
- orphanRemovel
- 상속관계 매핑
- 객제지향 쿼리 언어
Archives
- Today
- Total
hoondev
[Spring JPA] 값 타입 컬렉션 본문
값 타입 컬렉션
값 타입을 컬렉션에 담아서 사용할 수 있다.
컬렉션은 db에 매핑할때 컬렉션 용 테이블을 따로 생성해서 관리해야한다.
Address
@Embeddable
@Getter @Setter
public class Address {
@Column(name="city")
private String city;
private String street;
private String zipcode;
...
}
Member
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@Column(name = "username")
private String username;
@Embedded
private Address homeAddress;
@ElementCollection
@CollectionTable(name = "FAVORITE_FOOD", joinColumns =
@JoinColumn(name = "MEMBER_ID")
)
@Column(name = "FOOD_NAME") //컬럼명 설정
private Set<String> favoriteFoods = new HashSet<>();
@ElementCollection
@CollectionTable(name = "ADDRESS", joinColumns =
@JoinColumn(name = "MEMBER_ID")
)
private List<Address> addressesHistory = new ArrayList<>();
...
}
특징
- 값 타입을 하나 이상 저장할 때 사용
- @ElementCollection, @CollectionTable 사용
- 데이터베이스는 컬렉션을 같은 테이블에 저장할 수 없다.
- 컬렉션을 저장하기 위한 별도의 테이블이 필요함
저장
Member member = new Member();
member.setUsername("member1");
member.setHomeAddress(new Address("homeCity", "street", "10000");
member.getFavoriteFoods().add("치킨");
member.getFavoriteFoods().add("족발");
member.getFavoriteFoods().add("피자");
member.getAddressHistory().add(new Address("old1", "street1", "10000"));
member.getAddressHistory().add(new Address("old2", "street2", "10000"));
em.persist(member);
member만 persist해주면 값타입들은 자동으로 persist된다. (생명주기가 member에 소속)
cascade, orphanremoval 기능을 필수로 가진다고 볼 수 있다.
그리고 값타입 컬렉션은 지연로딩을 사용한다.
수정
값 타입 업데이트
Member findMember = em.find(Member.class, member.getId);
Address a = findMember.getHomeAddress();
findMember.setHomeAddress(new Address("newCity", a.getStreet(), a.getZipcode()));
사이드 이펙트가 발생할 수 있는 setter를 사용하지말고 새로운 인스턴스를 생성해서 깔아끼워주자.
값 타입 컬렉션 업데이트
findMember.getFavoriteFoods().remove("치킨");
findMember.getFavoriteFoods().add("한식");
findMember.getAddressHistory().remove(new Address("old1", "street1", "10000"));
findMember.getAddressHistory().add(new Address("newCity1", "street1", "10000"));
기존에 있던 값을 삭제하고 새로운 값을 추가해야 한다.
그럼 삭제 한번 인서트 한번 쿼리가 나갈것 같지만 아니다.
변경 사항이 발생하면, 주인 엔티티와 연관된 모든 데이터를 삭제하고, 값 타입 컬렉션에 있는 현재 값을 모두 다시 저장한다.
값 타입 컬렉션의 제약사항
- 값 타입은 엔티티와 다르게 식별자 개념이 없다.
- 값은 변경하면 추적이 어렵다.
- 값 타입 컬렉션에 변경 사항이 발생하면, 주인 엔티티와 연관된 모든 데이터를 삭제하고, 값 타입 컬렉션에 있는 현재 값을 모두 다시 저장한다.
- 값 타입 컬렉션을 매핑하는 테이블은 모든 컬럼을 묶어서 기본 키를 구성해야 함: null 입력X, 중복 저장X
값 타입 컬렉션 대안
실무에서는 상황에 따라 값 타입 컬렉션 대신에 일대다 관계를 고려하자.
일대다 관계를 위한 엔티티를 만들고, 여기에서 값 타입을 사용하자.
영속성 전이(Cascade) + 고아 객체 제거를 사용해서 값 타입 컬렉션 처럼 사용하자. EX) AddressEntity
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "MEMBER_ID")
private List<AddressEntity> addressHistory = new ArrayList<>();
@Entity
@Getter @Setter
@Table(name = "ADDRESS")
public class AddressEntity {
@Id @GeneratedValue
private Long id;
private Address address;
public AddressEntity() {
}
public AddressEntity(String city, String street, String zipCode) {
this.address = new Address(city, street, zipCode);
}
}
정리
엔티티 타입의 특징
- 식별자O
- 생명 주기 관리
- 공유
값 타입의 특징
- 식별자X
- 생명 주기를 엔티티에 의존
- 공유하지 않는 것이 안전(복사해서 사용)
- 불변 객체로 만드는 것이 안전
값 타입은 정말 값 타입이라 판단될 때만 사용 엔티티와 값 타입을 혼동해서 엔티티를 값 타입으로 만들면 안됨 식별자가 필요하고, 지속해서 값을 추적, 변경해야 한다면 그것 은 값 타입이 아닌 엔티티
Reference
'Spring JPA' 카테고리의 다른 글
[Spring JPA] 기본 문법과 쿼리 API (0) | 2023.02.13 |
---|---|
[Spring JPA] 객체지향 쿼리 언어 (0) | 2023.02.10 |
[Spring JPA] 값 타입의 비교 (0) | 2023.02.08 |
[Spring JPA] 값 타입과 불변 객체 (0) | 2023.02.07 |
[Spring JPA] 임베디드 타입(복합 값 타입) (1) | 2023.02.06 |
Comments