- 값 타입
ㅇ 기본값 타입
ㅇ 임베디드 타입 (복합값 타입) - 중요★
ㅇ 값 타입과 불변 객체
ㅇ 값 타입의 비교
ㅇ 값 타입 컬렉션 - 중요★
ㅇ 정리
- 기본값 타입
+ 엔티티 VS 값 타입
-
엔티티는 데이터가 변해도 식별자로 추적 가능 (@Entity)
-
값타입은 식별자가 없고 값만 있으므로 추적 불가능 (int, Integer, String)
-
값 타입 분류
- 기본값 타입
- 자바 기본 타입 (int,double)
- 래퍼 클래스 (Integer,Long)
- String
- 생명 주기를 엔티티에 의존(★)
- 회원을 삭제하면 이름, 나이 필드도 함께 삭제
- 값 타입은 공유하면 X (★)
- 회원 이름 변경 시 다른 회원의 이름도 함께 변경되면 안됨
- int double 같은 기본타입(primitive type)은 절대 공유 X
- 기본 타입은 항상 값이 복사함
- Integer 같은 래퍼 클래스나 String 같은 특수한 클래스는 공유 가능한 객체지만 변경 X
- 기본값 타입
-
- 임베디드 타입 (embedded type, 복합 값 타입)
- 좌표 등(x와 y를 같이 묶어서 사용하고 싶음)
- 새로운 값 타입을 직접 정의할 수 있음
- 주로 기본 값 타입을 모아서 만들어서 복합 값 타입이라고도 함
- int,String과 같은 값 타입
- 회원 엔티티는 이름, 근무 시작일, 근무 종료일, 주소 도시, 주소 번지, 주소 우편번호를 가진다.
- 근무 시작,종료일 / 주소 도시 ,번지,우편번호는 공통으로 쓸 수 있지 않을까?
- 장점
- 재사용
- 높은 응집도
- Period.isWork()처럼 해당 값 타입만 사용하는 의미있는 메소드를 만들 수 있음
- 임베디드 타입을 포함한 모든 값 타입은, 값 타입을 소유한 엔티티에 생명 주기를 의존함
- 객체와 테이블을 아주 세밀하게 매핑 하는 것이 가능해진다.
- @AttributeOverrides / @AttributeOverride 를 사용하면 한 엔티티에서 같은 값 타입을 사용할 수 있다.
- 임베디드 컬럼은 매핑한 컬럼 값은 모두 NULL
- 값 타입과 불변 객체
- 값 타입은 복잡한 객체 세상을 조금이라도 단순화 하려고 만든 개념이다. 따라서 값 타입은 단순하고 안전하게 다룰 수 있어야한다.
- 값 타입 공유 참조 (★)
- 임베디드 타입 같은 값 타입을 여러 엔티티에서 공유하면 위험함!! (한 곳이라도 값을 변경하면 다른 엔티티의 값도 변경됨) (★)
- 부작용(side effect) 발생
- 대신 값(인스턴스)을 복사해서 사용ㄱ
- 객체 타입의 한계
- 항상 값을 복사해서 사용하면 공유 참조로 인해 발생하는 부작용을 피할 수 있다.
- 문제는 임베디드 타입처럼 직접 정의한 값 타입은 자바의 기본 타입이 아니라 객체 타입이다.
- 자바 기본 타입에 값을 대입하면 값을 복사한다.
- 객체 타입은 참조 값을 직접 대입하는 것을 막을 방법이 없다.
- 객체의 공유 참조는 피할 수 없다.
- 불변 객체
- 객체 타입을 수정할 수 없게 만들면 부작용을 원천 차단
- 값 타입을 불변 객체(immutable object)로 설계해야함
- 불변 객체: 생성 시점 이후에 절대 값을 변경할 수 없는 객체
- 생성자로만 값을 설정하고 수정자(Setter)를 만들지 않으면 됨 (임베디드 타입에)
- 참고 : Integer,String은 자바가 제공하는 대표적인 불변 객체
- 불변이라는 작은 제약으로 부작용이라는 큰 재앙을 막을 수 있다. ※ 임베디드 타입을 Setter를 지움 / Setter 접근 제한자를 private으로 변경함으로써 불변객체를 만들고 side effect를 없앤다. ※ 값을 변경하고 싶으면 새로운 인스턴스를 만들어서 다시 집어넣는다. ※ 전부 다 무조건 불변으로 만들어라 (★★★)
- 값 타입 비교
- 동일성(identity) 비교 : 인스턴스의 참조 값을 비교 , ==사용
- 동등성비(equivalence) 비교 : 인스턴스의 값을 비교, equals() 사용
- 값 타입은 a.equals(b)를 사용해서 동등성 비교를 해야함
- 값 타입은 equals() 메소드를 적절하게 재정의(주로 모든 필드 사용) - @Override equals를 사용해서 비교해야함
- 값 타입 컬렉션 (collection value type)
- 값 타입 컬렉션이란?
- 자바 컬렉션에 기본 값 타입이나 입베디드 타입을 넣을 수 있는 것
- 자바에서는 컬렉션에 값 타입이나 임베디드 타입을 넣지만 디비에서는 그런 타입이 없음
- 최근에서야 디비에 JSON 타입이 도입 됐지만 기본적으로는 컬럼 하나에 하나의 값이 들어감
- 이를 해결하기 위해 리스트로 담을 타입은 테이블로 빼야함
- EX) LIST[Address] -> MEMBER를 ADDRESS테이블로 뺌
- 빼낸 DB에 값들을 전부다 묶어서 PK로 저장을 해야한다.
- 값 타입을 하나 이상 저장할 때 사용
- @ElementCollaction, @CollationTable을 사용
- DB에는 컬렉션을 같은 테이블에 저장할 수 없다. (리스트이기 때문에)
- 별도의 테이블이 필요하다 --------- + 값 타입 컬렉션 사용
- 값 타입 저장 예제
- 값 타입 컬렉션은 본인 스스로의 라이프 사이클이 없다. 엔티티에 의존해서 엔티티가 해당 컬렉션을 저장하면 값타입 컬렉션 테이블에 데이터가 저장된다.
- 값 타입 조회
- 값 타입 컬렉션은 기본적으로 지연 로딩이다!!!
- 값 타입 수정
- 값 타입 :(setCity,setStreet,setZipCode로 변경하면 전체 참조하는 게 다 바뀜) 해결: 새로운 객체를 넣어야한다.
- 값 타입 컬렉션 : Set[String] 일 경우, findUser.getFavoriteFoods().remove("족발"); -> findUser.getFavoriteFoods().add("한식");으로 remove 후 add List[Address] 일 경우, findUser.getAddressHistory().remove(new Address("old1","street1","zipcode1")); -> findUser.getAddressHistory().add(new Address("newCity1","street","zipcode")); 으로 객체를 통으로 바꿔줘야한다. ※ 이때 Address entity에서 equals를 제대로 override해야한다. 안그럼 망한다!!!! 중요!!!!★★★ (remove가 override받은 equals를 사용하기 때문)
- 값 타입 컬렉션은 영속성 전이(cascade) + 고아 객체 제거 기능을 필수로 가진다고 볼 수 있다.
- 값 타입의 제약 사항 (★★★)
- 값 타입은 엔티티와 다르게 식별자 개념이 없다.
- 값은 변경하면 추적이 어렵다.
- 값 타입 컬렉션에 변경 사항이 발생하면, 주인 엔티티와 연관된 모든 데이터를 삭제하고, 값 타입 컬렉션에 있는 현재 값을 모두 다시 저장한다. (★★★)
- 해결 : @OrderColumn을 사용해서 잡을 순 있지만 엄청 굉장히 위험하다!!! 원하지 않는 데이터가 들어올 수도 있고 중간에 하나 없어지면 null이 들어옴
- 값 타입 컬렉션을 매핑하는 테이블은 모든 컬럼을 묶어서 기본키를 구성해야함: null입력 x, 중복 저장 x
- 웬만하면 값타입 컬렉션을 사용하지 않는 게 좋음!!!! (★)
- 값 타입 컬렉션 대안
- 실무에서는 상황에 따라 값 타입 컬렉션 대신에 일대다 관계를 고려!!
- 값 타입을 래핑해서 엔티티로 승급한 후 oneToMany로 일대다 관계를 만들어서 사용한다. (★)
- 값 타입을 oneToMany로 엔테테로 한 번 래핑해서 값타입을 엔티티로 승급해서 사용
- 실제로 실무에서는 이 방법을 많이 사용함 (영한님)
- ex) addressEntity
- 값 타입 컬렉션을 언제 사용하느냐?
- 셀렉트 박스에 (치킨,햄버거,짜장면,피자 등)을 여러가지 선택할 수 있을 때
- 값이 단순할 때.(선호도?)
- 값이 바뀌어도 업데이트 칠 필요가 업을 떄
- 값을 추적하지 않아도 될 때
- 주소 이력 같은건 전부 다 엔티티임!!
- 값 타입 컬렉션이란?
- 정리
- 엔티티 타입의 특징
- 식별자 O
- 생명 주기 관리
- 공유
- 값 타입의 특징
- 식별자 X
- 생명주기를 엔티티에 의존
- 공유하지 않는 것이 안전(복사해서 사용)
- 불변 객체로 만드는 것이 안전
- 주의사항
- 값 타입은 정말 값 타입이라 판단 될 때만 사용!! ( EX.포지션(X,Y좌표))
- 엔티티와 값 타입을 혼동해서 엔티티를 값 타입으로 만들면 안됨
- 식별자가 필요하고, 지속해서 값을 추적, 변경해야한다면 그것은 값타입이 아닌 엔티티
- 엔티티 타입의 특징