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 |
Tags
- JPA 활용2
- API 개발 고급
- 불변 객체
- JPA
- 검증 애노테이션
- 기본문법
- 스프링 데이터 JPA
- jpa 활용
- 일론머스크
- 실무활용
- 스프링MVC
- JPQL
- 컬렉션 조회 최적화
- Bean Validation
- 김영한
- 트위터
- 벌크 연산
- 스프링 mvc
- 예제 도메인 모델
- 프로젝트 환경설정
- Spring Data JPA
- QueryDSL
- 임베디드 타입
- 타임리프
- 타임리프 문법
- 값 타입 컬렉션
- 로그인
- JPA 활용 2
- 스프링
- 페이징
Archives
- Today
- Total
RE-Heat 개발자 일지
[JPA] [9] 값 타입 (상편) 본문
https://www.inflearn.com/course/ORM-JPA-Basic
인프런 김영한 님의 강의를 듣고 작성한 글입니다.
■ JPA 데이터 타입 분류
① 엔티티 타입
- @Entity로 정의하는 객체
- 데이터가 변해도 식별자로 계속해 추적 가능
- 예) 회원 엔티티의 키나 나이 값을 변경해도 식별자로 인식 가능
② 값 타입
- int, Integer, String처럼 단순한 값으로 사용하는 자바 기본 타입 또는 객체
- 식별자가 없고 값만 있으므로 변경 시 추적 불가
- 예) 숫자 100을 200으로 변경하면 완전히 다른 값으로 대체됨
■ 값 타입 분류
① 기본값 타입
- 자바 기본 타입(int, double)
- 래퍼 클래스(Integer, Long)
- String
② 임베디드 타입(embedded type, 복합 값 타입)
- 예) x, y 좌표 묶어서 값으로 쓰고 싶을 때 쓰는 타입
③ 컬렉션 값 타입(collection value type)
- 자바 컬렉션에 기본 값이나 임베디드 타입을 넣은 형태다
[1] 기본값 타입
■ 종류
- 자바 기본 타입(int, double)
- 래퍼 클래스(Integer, Long)
- String
■ 특징
- 생명 주기를 엔티티에 의존한다.
- 예) 회원을 삭제하면 이름, 나이 필드도 함께 삭제된다.
- 값 타입은 공유하면 안 된다.
- 예) 회원 이름 변경 시 다른 회원의 이름도 함께 변경되면 안 된다.
참고] 자바의 기본 타입은 절대 공유되지 않는다.
int a = 20;
int b = a; //a객체의 주소값이 아닌 20이라는 값을 복사
b = 10;
- int, double 같은 기본 타입(primitive type)은 절대 공유되지 않는다.
- 기본 타입은 항상 값을 복사한다.
- 따라서 값을 공유한다고 해도 부작용이 일어나지 않는다.
- 주소값(=참조값)은 Stack영역이 가지고 있는 heap 영역의 객체 주소
Integer a = new Integer(10);
Integer b = a; // a의 참조를 복사
a.setValue(20); // 부작용 발생
- Integer 같은 래퍼 클래스나 String 같은 특수한 클래스는 공유 가능한 객체이지만 변경 X
- setValue()가 가능하면 a, b 모든 값이 바뀌는 불상사가 발생하나, 래퍼 클래스는 애초에 set을 못하게 막아 놓음
[2] 임베디드 타입
새로운 값 타입을 직접 정의하는 걸 JPA에선 임베디드 타입이라 부른다.
- 주로 기본 값 타입을 모아서 만들어서 복합 값 타입이라고도 함
- int, String과 같은 값 타입
■ 예시
- 기존 Member 테이블은 근무 시작·종료일, 주소의 도시·번지·우편 번호를 가지고 있음
- 비슷한 필드끼리 묶기로 결정. 근무 시작·종료일은 근무 기간으로 도시·번지·우편 번호는 집 주소로 변경
- 이런 식으로 묶는 것을 임베디드 타입이라고 부른다.
■ 임베디드 타입의 장점
- 재사용이 가능하다.
- 클래스 내 응집도가 높다.
- Period.isWork()처럼 해당 값 타입만 사용하는 의미 있는 메소드를 만들 수 있다.
- 임베디드 타입을 포함한 모든 값 타입은, 값 타입을 소유한 엔티티에 생명주기를 의존한다.
■ 임베디드 타입과 테이블 매핑
- 임베디드 타입은 엔티티의 값일 뿐이다.
- 임베디드 타입을 사용하기 전과 후에 매핑하는 테이블은 같다.
- 테이블 입장에선 임베디드 타입을 써도 바뀌는 게 없다.
- 단, 객체는 데이터 외에 메서드(period.isWork() 등)까지 들고 있으므로 이득이 많다.
- 객체와 테이블을 아주 세밀하게(find-grained) 매핑하는 것이 가능
- 잘 설계한 ORM 애플리케이션은 매핑한 테이블의 수보다 클래스의 수가 더 많음
■ 기본 사용법
- @Embeddable : 값 타입을 정의하는 곳에 표시한다.
- @Embedded : 값 타입을 사용하는 곳에 표시한다.
- 기본 생성자는 필수이다.
참고] @Embeddable, @Embedded는 둘 중 하나만 명시해도 되나 둘 다 적는 게 낫다.
- 값 타입을 사용하는 MemberEmb에 @Embedded를 명시한다.
- 값 타입을 정의하는 Period, Address에 @Embeddable를 넣는다.
참고] 기존 Member 객체는 연관된 게 많아서 MemberEmb 객체를 따로 만듦
■ 임베디드 타입과 연관관계
- 임베디드 타입이 임베디드 타입을 가질 수 있다. (Address → Zipcode)
- 임베디드 타입이 엔티티 가질 수 있다. (PhoneNumber → PhoneEntity)
- FK만 가지면 되기 때문이다.
■ @AttributeOverride: 속성 재정의
한 엔티티에서 같은 값 타입을 쓰면 컬럼명이 중복된다. 이때 쓰는 애노테이션이다.
@Entity
public class Member {
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "city",
column = @Column(name = "WORK_CITY")),
@AttributeOverride(name = "street",
column = @Column(name = "WORK_STREET")),
@AttributeOverride(name = "zipcode",
column = @Column(name = "WORK_ZIPCODE"))
})
private Address workAddress;
}
- homeAdress는 기존 컬럼명이 그대로 저장되고, workAddress는 새로 정의된 컬럼명을 사용한다.
- @AttribteOverrides 안에 @AttributeOverride를 지정해 주는 걸 헷갈리지 말자(es 복수형 잊지 말자)
결과 : city, street, zipcode 외에 WORK_CITY, WORK_STREET, WORK_ZIPCODE도 MemberEmb 테이블에 삽입
@AttributeOverride로 매핑 정보를 재정의 하지 않으면?
[3] 값 타입과 불변 객체
값 타입은 복잡한 객체 세상을 조금이라도 단순화하려고 만든 개념이다. 따라서 값 타입은 단순하고 안전하게 다 룰 수 있어야 한다.
■ 값 타입 공유 참조의 부작용
- 임베디드 타입 같은 값 타입을 여러 엔티티에서 공유하면 위험하다.(부작용 발생)
▶ 예시
- member와 member2는 address를 공유하고 있다.
- member.getHomeAddress().setCity("newCity")로 첫 번째 member의 city값만 바꾸는 게 의도
- 하지만 실제 결과는 member와 member2의 city값이 newCity로 변경된다.(update 쿼리문 두 번 날아감)
■ 값 타입 공유 참조 부작용을 막는 해결책
값 타입의 실제 인스턴스인 값을 공유하는 건 위험하므로 대신 값을 복사해서 사용하면 된다.
▶ 예시
- Address를 복사해서 member2 값에 넣고 setCity()로 첫 번째 것만 바꾼다.
- member2는 복사한 인스턴스(주소값이 다름)를 들고 있으므로 영향이 없다.
■ 객체 타입의 한계
- 항상 값을 복사해서 사용하면 공유 참조로 인해 발생하는 부작용을 피할 수 있다.
- 문제는 임베디드 타입처럼 직접 정의한 값 타입은 자바의 기본 타입이 아니라 객체 타입이다.
- 자바 기본 타입에 값을 대입하면 값을 복사한다.
- 객체 타입은 참조 값을 직접 대입하는 것을 막을 방법이 없다.
- 객체의 공유 참조는 피할 수 없다.
한계를 벗어나는 해결책은 바로 불변객체를 만드는 것이다.
■ 불변 객체(Immutable Object)
객체 타입을 수정할 수 없게 만들면 부작용을 원천 차단할 수 있다
- 값 타입은 불변 객체(immutable object)로 설계해야 한다.
- 불변 객체: 생성 시점 이후 절대 값을 변경할 수 없는 객체
- 생성자로만 값을 설정하고 수정자(Setter)를 만들지 않으면 됨
- 참고: Integer, String은 자바가 제공하는 대표적인 불변 객체
▶ 예시
- Address에 값을 수정할 수 있는 setter를 쓰지 않거나 private으로 만들어버리면 된다.
=> 그렇다면 값을 바꾸는 방법은?
- Address를 통째로 갈아엎으면 된다.
Address address = new Address("city", "street", "10000");
Member member = new Member();
member.setUsername("member1");
member.setHomeAddress(address);
em.persist(member);
Address newAddress = new Address("NewCity", address.getStreet(), address.getZipcode());
member.setHomeAddress(newAddress); // 통째로 변경
tx.commit();
'백엔드 > JPA' 카테고리의 다른 글
[JPA] [10] 객체지향 쿼리 언어 - 기본 문법 (상편) (0) | 2023.08.16 |
---|---|
[JPA] [9] 값 타입 (하편) (0) | 2023.08.13 |
[JPA] [8] 프록시와 연관관계 관리 (0) | 2023.08.12 |
[JPA] [7] 고급 매핑 (0) | 2023.08.11 |
[JPA] [6] 다양한 연관관계 매핑 (0) | 2023.08.10 |