일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- API
- Baekjoon
- 백준
- 개발
- 애자일
- Java
- 애자일프로그래밍
- 읽기쉬운코드
- ES
- 알고리즘
- JPA
- Spring
- 그리디
- database
- 클린코드
- 개발자
- 그리디알고리즘
- 자바
- cleancode
- 코딩테스트
- 데이터베이스
- 스프링
- spring boot
- Elasticsearch
- 애자일기법
- 엘라스틱서치
- 프레임워크
- 코딩
- 코드
- framework
- Today
- Total
튼튼발자 개발 성장기🏋️
값 타입 본문
1. 기본값 타입
기본값 타입은 자바의 원시 타입과 그에 대응되는 래퍼 클래스, 그리고 String 같은 값 클래스들로, JPA에서는 이러한 타입을 값 타입이라고 부른다. 예를 들어, int, Integer, String, boolean 등이 기본값 타입에 속합니다. 이러한 타입들은 데이터베이스의 컬럼에 매핑되어 저장되며, JPA가 제공하는 생명주기를 따르지 않고, 불변 객체로 다루는 것이 일반적이다.
2. 임베디드 타입
임베디드 타입(Embeddable Type)은 여러 개의 기본값 타입을 모아서 하나의 복합적인 값 타입을 만드는 것이다. 이 임베디드 타입은 엔티티의 일부로 사용되며, 엔티티의 속성으로 포함되어 데이터베이스 테이블의 컬럼에 매핑된다. 예를 들어, 주소(Address)와 같은 객체는 도시, 거리, 우편번호와 같은 여러 속성을 가질 수 있는데, 이들을 모아서 하나의 임베디드 타입으로 정의할 수 있다.
2.1. 임베디드 타입과 테이블 매핑
임베디드 타입은 별도의 테이블을 만들지 않고, 엔티티가 매핑된 테이블에 속성들이 컬럼으로 함께 매핑된다. 예를 들어, 주소(Address)라는 임베디드 타입이 있다면, Address 객체의 속성인 도시(city), 거리(street), 우편번호(zipcode)가 엔티티가 매핑된 테이블의 컬럼으로 추가된다.
@Embeddable
public class Address {
private String city;
private String street;
private String zipcode;
}
@Entity
public class Member {
@Id
@GeneratedValue
private Long id;
private String name;
@Embedded
private Address address;
}
2.2. 임베디드 타입과 연관관계
임베디드 타입은 기본적으로 값 타입이기 때문에, 다른 엔티티와 연관관계를 가질 수 없다. 연관관계는 오직 엔티티 간에만 설정할 수 있으며, 임베디드 타입 내에 엔티티 타입의 필드를 포함하는 것은 권장되지 않는다고한다.
@Entity
public class City {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
public City() {}
public City(String name) {
this.name = name;
}
}
@Embeddable
public class Address {
private String street;
private String zipcode;
@ManyToOne
@JoinColumn(name = "city_id")
private City city;
public Address() {}
public Address(String street, String zipcode, City city) {
this.street = street;
this.zipcode = zipcode;
this.city = city;
}
}
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@Embedded
private Address address;
public Member() {}
public Member(String name, Address address) {
this.name = name;
this.address = address;
}
}
2.3. @AttributeOverride: 속성 재정의
같은 임베디드 타입을 여러 엔티티에서 재사용할 때, 테이블 매핑에서 충돌이 발생하지 않도록 컬럼 명을 재정의할 수 있다. 이때 사용하는 것이 @AttributeOverride 어노테이션이다. 이를 통해 동일한 임베디드 타입을 사용하는 경우에도 각기 다른 컬럼 명으로 매핑할 수 있다.
@Entity
public class Member {
@Id
@GeneratedValue
private Long id;
private String name;
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "city", column = @Column(name = "home_city")),
@AttributeOverride(name = "street", column = @Column(name = "home_street")),
@AttributeOverride(name = "zipcode", column = @Column(name = "home_zipcode"))
})
private Address homeAddress;
@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;
}
3. 값 타입과 불변 객체
값 타입은 공유 참조에 의해 원치 않는 사이드 이펙트를 방지하기 위해 불변 객체로 설계하는 것을 권장한다. 값 타입이 불변 객체로 설계되면, 값을 변경할 수 없고, 상태 변경으로 인한 버그를 예방할 수 있다.
3.1. 값 타입 공유 참조
값 타입을 여러 엔티티에서 공유하게 되면, 한 곳에서 값을 변경했을 때 다른 곳에도 영향을 미칠 수 있는 문제가 발생한다. 이를 "값 타입 공유 참조" 문제라고 한다. 이는 기본적으로 값 타입이 동일한 객체 참조를 공유하기 때문에 발생한다.
Address address = new Address("Seoul", "Main Street", "12345");
Member member1 = new Member("John", address);
Member member2 = new Member("Jane", address);
member1.getAddress().setCity("Busan");
System.out.println("Member1's City: " + member1.getAddress().getCity()); // Busan
System.out.println("Member2's City: " + member2.getAddress().getCity()); // Busan
3.2. 값 타입 복사
값 타입 공유 참조 문제를 해결하기 위해, 값 타입을 사용할 때는 객체를 복사(clone)해서 사용해야 한다. 이를 통해 원본 객체와는 다른 독립적인 객체로 사용하여 값 변경이 다른 곳에 영향을 미치지 않도록 할 수 있다.
3.3. 불변 객체
불변 객체(Immutable Object)는 String이나 Integer와 같이 객체의 상태를 변경할 수 없는 객체를 말한다. JPA에서 값 타입을 불변 객체로 사용하면, 한 번 생성된 이후에는 그 상태가 변하지 않으므로 안전하게 사용할 수 있다. 불변 객체를 만드는 방법으로는 객체의 모든 필드를 final로 선언하고, 생성자에서 초기화하여 setter를 생성하지 않도록 할 수 있다.
@Embeddable
@Getter
public class Address {
private final String city;
private final String street;
private final String zipcode;
public Address(String city, String street, String zipcode) {
this.city = city;
this.street = street;
this.zipcode = zipcode;
}
}
4. 값 타입의 비교
값 타입의 비교는 객체 참조가 아닌 객체의 내부 값(내용)을 기준으로 비교해야 한다.(동등성 비교) 이는 equals와 hashCode 메서드를 적절히 재정의하여 사용해야한다. JPA는 값 타입의 비교를 할 때, 내용 기반 비교를 사용하도록 권장한다.
5. 값 타입 컬렉션
값 타입 컬렉션은 값 타입을 컬렉션 형태로 사용하는 것을 의미한다. 예를 들어, Member 엔티티가 여러 개의 Address를 가질 때, 이를 값 타입 컬렉션으로 정의할 수 있다. 이때 값 타입 컬렉션은 별도의 테이블에 매핑된다.
5.1. 값 타입 컬렉션 사용
값 타입 컬렉션은 @ElementCollection 어노테이션을 사용하여 정의한다. 이때 JPA는 엔티티와 값 타입 컬렉션의 매핑을 위해 별도의 테이블을 생성한다.
@Entity
public class Member {
@Id
@GeneratedValue
private Long id;
private String name;
@ElementCollection
@CollectionTable(name = "member_address", joinColumns = @JoinColumn(name = "member_id"))
private List<Address> addresses = new ArrayList<>();
}
5.2. 값 타입 컬렉션의 제약사항
값 타입 컬렉션은 엔티티와 다르게 생명주기를 따르지 않기 때문에, 값 타입 컬렉션에 대한 제약사항이 존재한다. 주의해야 할 주요 제약사항은 다음과 같다.
- 값 타입 컬렉션은 기본적으로 영속성 전이(Cascade)와 고아 객체 제거(Orphan Removal)를 지원한다.
- 값 타입 컬렉션을 사용할 때는 지연 로딩(Lazy Loading)이 기본 전략으로 사용된다.
- 값 타입 컬렉션은 대규모 데이터 처리에 비효율적일 수 있으므로, 컬렉션의 크기가 매우 클 경우에는 엔티티를 사용하여 매핑하는 것이 좋다.
'API > JPA' 카테고리의 다른 글
객체지향 쿼리 심화 (0) | 2024.09.08 |
---|---|
객체지향 쿼리: JPQL (1) | 2024.09.07 |
프록시와 연관관계 관리 (0) | 2024.08.27 |
Deep한 JPA 매핑 (0) | 2024.08.24 |
연관관계 매핑 기초 (0) | 2024.08.18 |