728x90
반응형
값 타입 컬렉션
- 값 타입을 하나 이상 저장 할 때 사용한다.
- 즉 List 나 Set 을 이용하여 저장하는 것을 말한다.
컬렉션을 DB에 어떻게 표현을 해야 될까?
- DB 에는 자료구조 개념이 없기 때문에
- 1:N 으로 풀어야 된다.
예시
- 멤버에는 좋아하는 음식들 그리고 내가 주문했던 위치들을 있다고 해보자
- 다음과 같이 표현할수 있다.
값타입 어노테이션
- @ElementCollection:
- 컬렉션 객체임을 JPA 에게 알려준다. - @CollectionTable(name = "ADDRESS", joinColumns = @JoinColumn(name = "MEMBER_ID") ) :
- 새롭게 생성되는 테이블에 대한 정보를 입력하는 어노테이션이다.
- 컬럼 이름은 ADDRESS 이고 FK 는 MEMBER_ID 로 설정해주었다.
값타입 코드 구현
코드의 간결성을 위해 해당 부분에서는 getter 와 setter 를 뺐습니다.
값타입 컬렉션에 @ElementCollection 을 선언해준다.
@Entity
public class Member extends BaseEntity {
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USER_NAME")
private String userName;
@Embedded
private Period workPeriod;
@Embedded
private Address homeAddress;
// 값 컬렉션 타입
// 값 타입 맵핑
@ElementCollection
// 테이블 명과 조안 FK 지정
@CollectionTable(name = "FAVORITE_FOOD", joinColumns =
@JoinColumn(name = "MEMBER_ID")
)
@Column(name = "FOOD_NAME") // 예외적으로 된다. 1나이기 때문에 컬럼명 지정가능
private Set<String> favoriteFoods = new HashSet<>();
// 값 타입 맵핑
@ElementCollection
// 테이블 명과 조인 FK 지정
@CollectionTable(name = "ADDRESS", joinColumns =
@JoinColumn(name = "MEMBER_ID")
)
private List<Address> addressesHistory = new ArrayList<>();
}
값타입 컬렉션 저장 구현
기존 컬렉션을 사용하는것 처럼 쓰면된다.
public class jpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
// embedded collection ex
Member member = new Member();
member.setUserName("1");
member.setHomeAddress(new Address("homeCity","street", "zipcode"));
member.getFavoriteFoods().add("치킨");
member.getFavoriteFoods().add("족발");
member.getFavoriteFoods().add("피자");
member.getAddressesHistory().add(new Address("old1","street","zipCode"));
member.getAddressesHistory().add(new Address("old2","street","zipCode"));
em.persist(member);
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
}
emf.close();
}
}
값타입 컬렉션 조회
- 기존 컬렉션을 사용하는것 처럼 쓰면된다.
- 참고로 멤버만 조회하면 컬렉션 에대한 쿼리는 나가지 않는다.
- 이유는 값타입 컬렉션은 LAZY 이기 때문이다. - 그렇기 때문에 컬렉을 직접 사용할때 쿼리가 나간다.
public class jpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
// embedded collection ex
Member member = new Member();
member.setUserName("1");
member.setHomeAddress(new Address("homeCity","street", "zipcode"));
member.getFavoriteFoods().add("치킨");
member.getFavoriteFoods().add("족발");
member.getFavoriteFoods().add("피자");
member.getAddressesHistory().add(new Address("old1","street","zipCode"));
member.getAddressesHistory().add(new Address("old2","street","zipCode"));
em.persist(member);
//DB 저장후 영속성 클리어
em.flush();
em.clear();
// 조회 했을 경우 멤버만 조회 한다.
// 컬렉션 값 타입을 조회하지 않는다.
// 이유는? 컬렉션 값타입은 지연로딩이다.
System.out.println("==================== Start ====================");
Member findMember = em.find(Member.class, member.getId());
// 값 타입 조회
// 가져올때 쿼리가 수행된다.
List<Address> addressesHistory = findMember.getAddressesHistory();
for (Address address : addressesHistory) {
System.out.println("address: " + address.getCity());
}
// 가져올때 쿼리가 수행된다.
Set<String> favoriteFoods = findMember.getFavoriteFoods();
for(String favoriteFood : favoriteFoods) {
System.out.println("favoriteFood: " + favoriteFood);
}
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
}
emf.close();
}
}
값타입 컬렉션 수정
- 값타입 수정은 전체를 다 변경해줘야한다.
- 비교시 equals 를 사용하기 때문에 equals 와 hasCode 를 구현해야 한다.
public class jpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
// embedded collection ex
Member member = new Member();
member.setUserName("1");
member.setHomeAddress(new Address("homeCity","street", "zipcode"));
member.getFavoriteFoods().add("치킨");
member.getFavoriteFoods().add("족발");
member.getFavoriteFoods().add("피자");
member.getAddressesHistory().add(new Address("old1","street","zipCode"));
member.getAddressesHistory().add(new Address("old2","street","zipCode"));
em.persist(member);
//DB 저장후 영속성 클리어
em.flush();
em.clear();
// 조회 했을 경우 멤버만 조회 한다.
// 컬렉션 값 타입을 조회하지 않는다.
// 이유는? 컬렉션 값타입은 지연로딩이다.
System.out.println("==================== Start ====================");
Member findMember = em.find(Member.class, member.getId());
// 값 타입 조회
// 가져올때 쿼리가 수행된다.
List<Address> addressesHistory = findMember.getAddressesHistory();
for (Address address : addressesHistory) {
System.out.println("address: " + address.getCity());
}
// 가져올때 쿼리가 수행된다.
Set<String> favoriteFoods = findMember.getFavoriteFoods();
for(String favoriteFood : favoriteFoods) {
System.out.println("favoriteFood: " + favoriteFood);
}
// 값 타입 수정
// homeCity -> newCity
// 아래 처럼 수정하면 안된다. 사이드 이펙트 발생
// findMember.getHomeAddress().setCity("newCity");
// 통으로 변경해야 된다.
Address old = findMember.getHomeAddress();
findMember.setHomeAddress(new Address("newCity", old.getStreet(), old.getZipCode()));
// 값타입 컬렉션 수정
// 치킨 -> 한식으로 변경
// 스트링이기 때문에 삭제후 변경해야된다.
//findMember.getFavoriteFoods().remove("치킨");
//findMember.getFavoriteFoods().add("한식");
// 주소를 변경한다.
// old1 -> new 1 으로 변경
// 찾을때 equals 를 사용한다. equals 와 hascode 를 잘 짜줘야한다.
findMember.getAddressesHistory().remove(new Address("old1","street","zipCode"));
findMember.getAddressesHistory().add(new Address("newCity","street","zipCode"));
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
}
emf.close();
}
}
전체코드
https://github.com/rnrl1215/jpa-study/tree/main/src/main/java/jpabook/jpashop
참고:
https://www.inflearn.com/course/ORM-JPA-Basic/lecture/21716
728x90
반응형
'DataBase > JPA' 카테고리의 다른 글
[JPA] JPQL 문법 (0) | 2021.09.23 |
---|---|
[JPA] JPQL 이란? (0) | 2021.09.23 |
[JPA] 값타입 (0) | 2021.08.22 |
[JPA] CASCADE (0) | 2021.08.22 |
[JPA] Lazy 와 Eager (0) | 2021.08.19 |
댓글