Free Lines Arrow
본문 바로가기
DataBase/JPA

[JPA] 값타입 컬렉션

by skahn1215 2021. 9. 17.
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

 

GitHub - rnrl1215/jpa-study

Contribute to rnrl1215/jpa-study development by creating an account on GitHub.

github.com

 

 

 

 

참고:

https://www.inflearn.com/course/ORM-JPA-Basic/lecture/21716

 

자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런 | 학습 페이지

지식을 나누면 반드시 나에게 돌아옵니다. 인프런을 통해 나의 지식에 가치를 부여하세요....

www.inflearn.com

 

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

댓글