개발놀이터

값 타입 본문

JPA/JPA

값 타입

마늘냄새폴폴 2021. 8. 26. 06:37

*값 타입

JPA의 데이터 타입 분류에는 두가지 타입이 있다.
1. 엔티티 타입
2. 값 타입

1. 엔티티 타입 특징
1-1. @Entity로 정의하는 객체
1-2. 데이터가 변해도 식별자로 지속해서 추적 가능 (PK)
1-3. ex) 회원 엔티티의 키나 나이 값을 변경해도 식별자로 추적 가능

2. 값 타입 특징
2-1. int ,Integer, String 처럼 단순히 값으로 사용하는 자바 기본 타입이나 객체
2-2. 식별자가 없고 값만 있으므로 변경시 추적 불가
2-3. ex) 숫자 100을 200으로 변경하면 완전히 다른 값으로 대체

값 타입에는 세가지 분류가 있다
1. 기본값 타입
2. 임베디드 타입
3. 컬렉션 타입

1. 기본값 타입
자바에서 흔히 사용하는 타입으로 자바 기본타입인 int, double이 있고 래퍼 클래스인 Integer, Long 특수클래스인 String이 있다.

2. 임베디드 타입
임베디드 타입은 새로운 값 타입을 직접 정의할 수 있고, JPA는 임베디드 타입에 속한다. 주로 기본 값 타입을 모아서 만들어서 복합 값 타입이라고도 한다. 주로 임베디드 타입은 객체상에서 묶을만한 것들을 묶어서 하나로 따로 분류하는 방식이다. 예를 들면 회원 엔티티에 이름, 도시, 번지, 우편번호를 가진다고 한다면 도시, 번지, 우편번호만 따로 모아서 address라는 큰 묶음으로 만들 수 있을 것이다. 이렇게 한다면 더욱 객체지향적으로 만들 수 있다.

임베디드 타입을 사용하는 방법
1. 먼저 묶음으로 만들 수 있는 클래스를 만든다. ex) Address 그리고 클래스 레벨에 @Embeddable이라는 어노테이션을 달아준다.
2. 그리고 필드에 원하는 속성을 넣는다. ex) private String city; / private String street; / private String zipcode; 
3. getter / setter를 만드는데 setter는 private 으로 외부에서의 접근을 막는다.
4. 필드에 있는 모든 변수들의 생성자를 만든다.
5. 기본 생성자를 만든다.
6. equals를 만든다.
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡAddress 클래스ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
7. 이 해당 임베디드 타입을 사용하고 싶은 클래스에서 선언해준다. ex) private Address address;
8. 이 변수 위에 @Embedded 어노테이션을 달아준다.
9. member.setAddress(new Address("Seoul", "WorldCupStreet", "06125")); 이런식으로 사용하면 끝

왜 setter 를 private 으로 외부접근을 막을까?
만약 임베디드 타입을 수정하고 싶을 때 Address address = member.getAddress();로 꺼내서 address.setCity("newCity"); 라고 바꾸면 내가 저장했던 모든 Address들은 전부 다 newCity로 바뀌게 된다. 이는 객체 타입이 가지고 있는 한계점이다. 난 하나만 바꾸고 싶었는데 모든 것이 바뀌게 되는 것이다. 이를 컴파일 단계에서 원천 차단하는 것이 다른 개발자가 잘 모르는 상태에서 어 이 address 바꿔도 되는 값인가보네 하고 setCity("newCity") 이거 하는 순간 모든 값이 바뀌어버리는 대참사가 일어난다. 심지어 update쿼리는 잘 날아가고 오류가 뜨지 않기 때문에 이건 겉잡을 수 없는 버그로 이어지게 된다. 때문에 생성하거나 수정하는 것은 오로지 생성자를 통해서만 가능하도록(불변 객체로 설계하도록) 하는 것이 적절하다.

임베디드 타입을 수정하고 싶으면?
Member findMember = em.find(Member.class, member.getId());
Address oldAddress = findMember.getAddress();
findMember.setAddress(new Address("newCity", oldAddress.getStreet(), oldAddress.getZipcode());
이렇게 직접 한땀한땀 바꿔줘야한다.

임베디드 타입의 장점
1. 재사용 가능
2. 높은 응집도
3. 해당 값 타입만 사용하는 의미있는 메소드를 만들 수 있음
4. 임베디드 타입을 포함한 모든 값 타입은, 값 타입을 소유한 엔티티에 생명주기를 의존함.



3. 값 타입 컬렉션
디비에서는 컬렉션을 사용할 수 없다. 때문에 컬렉션을 이용하려면 별도의 테이블이 있어야 한다. ORM은 마치 디비에서 컬렉션을 사용하는 느낌을 주는 기능을 제공한다.

값 타입 컬렉션의 특징
3-1. 값 타입을 하나 이상 저장할 때 사용
3-2. @ElementCollection, @CollectionTable 사용
3-3. 데이터베이스는 컬렉션을 같은 테이블에 저장할 수 없다.
3-4. 컬렉션을 저장하기 위한 별도의 테이블이 필요하다.

값 타입 컬렉션의 사용
1. 먼저 변수를 생성한다. private Set<String> favoriteFood; or private List<String> favoriteFood; 
2. 이 속성이 값 타입 컬렉션이라는 것을 JPA에게 알려줘야 한다. @ElementCollection 사용
3. 이 컬렉션이 들어갈 별도의 테이블을 매핑해준다. @CollectionTable(name = "favortie_food", joinColumn = @JoinColumn(name = "member_id"))
4. 필요하다면 칼럼 매핑까지 해준다. @Column(name = "favorite_food") 웬만하면 디비에 저장할 때는 언더스코어 방식을 사용

값 타입 컬렉션의 제약사항
-값 타입은 엔티티와 다르게 식별자 개념이 없다.
-때문에 값을 변경하면 추적하기가 어렵다.
-값 타입 컬렉션에 변경 사항이 발생하면, 주인 엔티티와 연관된 모든 데이터를 삭제하고, 값 타입 컬렉션에 있는 현재 값을 모두 다시 저장한다.
-값 타입 컬렉션을 매핑하는 테이블은 모든 칼럼을 묶어서 기본 키를 구성해야 한다 = null입력X, 중복저장X

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

이 상황에서는 Entity를 새로 만들고 일대다를 매핑하는데
1. AddressEntity라는 엔티티를 만든다.
2. AddressEntity안에 private Long id; / private Address address를 만들고 id는 기본키 매핑을 address에는 @Embedded를 달아준다.
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡAddressEntity클래스ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
3. 사용하고 싶은 클래스에 선언한다. List<AddressEntity> address = new ArrayList<>();
4. 일대다 매핑을 하고 캐스케이드와 orphanRemoval를 true로 한다. @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) / @JoinColumn(name = "member_id") 
5. 다용도 메서드를 만든다. 
public void changeAddress

'JPA > JPA' 카테고리의 다른 글

JPQL 중급  (0) 2021.08.26
JPQL 초급  (0) 2021.08.26
지연로딩과 즉시로딩 (프록시)  (0) 2021.08.24
JPA 상속관계 매핑  (0) 2021.08.23
JPA 연관관계의 주인  (0) 2021.08.23