728x90
반응형
JPA의 데이터 타입
- JPA의 데이터 분류는 엔티티 타입과 값 타입으로 크게 분류 된다.
- 값타입은 다양하지만 기본값 타입과 임베디드 타입이 중요하다.
엔티티 타입
- @Entity 로 정의하는 객체이다.
- 데이터가 변해도 식별자로 지속해서 추적이 가능하다.
값 타입
- int, integer, String 처럼 단순히 값으로 사용하는 자바 기본 타입이다.
- 식별자가 없다.
- 그렇기 때문에 변경시 추적이 불가하다.
값타입 분류
1. 기본값 타입
- 자바 기본 타입(int, double)
- 래퍼 클래스(Integer, Long)
- String
- 생명주기를 엔티티에 의존한다.
- 회원을 삭제하면 String name등 같이 삭제된다.
2. 임베디드 타입
- 복합값 타입
- 새로운 값 타입을 직접 정의할 수 있다.
- 기본 생성자가 필수이다.
3. 컬렉션 값 타입
임베디드 타입
새로운 값 타입을 직접정의할 수 있다.
JPA는 임베디드 타입이라한다.
주로 기본 값 타입을 모아 복합값 타입이라고도 한다.
예제
- 회원엔티티는 다음과 같은 정보를 가진다.
- 이름, 근무시작일, 근무종료일, 주소 도시, 주소 번지, 주소 우편번호
- 기존 멤버엔티티가 너무 복잡하니 다음과 같이 정리를 해준다.
- startDate, endDate 를 Period 임베디드 타입으로 만든다.
- city, street, zipcode를 address 임베디드 타입으로만든다.
다음과 같이 간편해졌다.
임베디드 타입 사용법
- @Embeddable: 값 타입을 정의 하는곳에 사용한다.
- @Embedded: 값 타입을 사용하는 곳에 표시한다.
- 기본생성자는 필수 이다
코드
Address
@Embeddable
public class Address {
private String city;
private String street;
private String zipcode;
// 기본생성자는 필수 이다.
public Address() {
}
public Address(String city, String street, String zipCode) {
this.city = city;
this.street = street;
this.zipcode = zipCode;
}
}
Period
@Embeddable
public class Period {
// 기본생성자는 필수이다.
public Period() {
}
public Period(LocalDateTime startDate, LocalDateTime endDate) {
this.startDate = startDate;
this.endDate = endDate;
}
private LocalDateTime startDate;
private LocalDateTime endDate;
}
member
@Entity
public class Member extends BaseEntity {
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(length = 10)
private String name;
@Embedded
private Period workPeriod;
@Embedded
private Address homeAddress;
}
임베디드 타입의 장점
- 재사용
- 높은 응집도
- Period.isWork() 처럼 의미있는 메소드를 만들수 있다.
임베디드 타입과 테이블 매핑
- 임베디드 타입은 엔티티의 값일 뿐이다.
- 임베디드 타입을 사용하기 전과 후에는 매핑하는 테이블은 같다.
- 잘설계한 ORM 애플리케이션은 매핑한 테이블의 수보다 클래스의 수가 더 많다.
임베디드 타입과 연관관계
- 임베디드 타입은 임베디드 타입을 가질수 있다.
- 임베디드 타입은 엔티티를 가질수 있다.
임베디드 타입중복사용
- 만약 주소를 집주소, 회사주소 두개가 필요 하다면
- 다음과 같이 임베디드 타입을 두번 선언하면 된다.
- 하지만 에러가 발생한다.
- 동일한 컬럼값이 중복 정의가 되었기 때문이다.
임베디드 타입의 중복사용 문제해결방법
두개의 속성을 사용하면 해결된다.
단순하게 컬럼명을 변경해 주는것이다.
- @AttributeOverrides
- @AttributeOverride
코드
@Entity
public class Member extends BaseEntity {
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(length = 10)
private String name;
@Embedded
private Period workPeriod;
@Embedded
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;
}
값 타입과 불변객체
- 값타입은 복잡한 객체세상을 단순화 하려고 한다.
값타입 공유 참조
- 임베디드 타입 같은 값 타입을 여러 엔티티에서 공유하면 위험함 부작용 발생
값타입 공유예제
- 회원1과 회원2가 임베디드 타입으로 CIty를 가지고 있을 경우를 생각해보자
- 만약 City 를 newCity로 변경하면? 회원1 과 회원2 모두 값이 변경이 된다.
- 그렇기 때문에 값타입을 공유하지 말고 값을 복사해서 사용해야 된다.
- 그래도 사이드이펙트가 발생할수 있다.
Address address = new Address("city","street","10000");
Member member1 = new Member();
member1.setUserName("member1");
member1.setHomeAddress(address);
em.persist(member1);
Member member2 = new Member();
member2.setUserName("member2");
member2.setHomeAddress(address);
em.persist(member2);
잘못된 예
// 첫번째 멤버의 주소만 newCity로 변경하고 싶다.
// 둘다 바뀐다.
member1.getHomeAddress().setCity("newCity");
복사해서 저장한예
// 값을 복사해서 해결해준다.
Address copyAddress = new Address(address.getCity(),address.getStreet(), address.getZipCode());
member1.setHomeAddress(copyAddress);
member1.getHomeAddress().setCity("newCity");
복사해도 사이드이펙트카 발생하는경우
- 누군가 실수로 복사로 안하고 쓰면 역시 사이드이펙트가 발생한다.
- 막을수 있는 방법이 없다.
불변객체로만든다.
- 처음부터 Address 의 값을 변경하지 못하게 setter를 다 지운다.
값타입의 비교
- 인스턴스가 달라도 그 안에 값이 같으면 같은 것으로 봐야된다.
- 값타입은 동등성 비교를 해야된다.
- 아래예제는 false 가 뜬다.
Address address1 = new Address("city","street","10000");
Address address2 = new Address("city","street","10000");
// false 출력
System.out.println("address1 == address2: " + (address1==address2));
System.out.println("address1 == address2: " + address1.equals(address2));
equals 와 hashcode 를 오버라이딩 해준다.
public class Address {
private String city;
private String street;
private String zipcode;
// 기본생성자는 필수 이다.
public Address() {
}
public Address(String city, String street, String zipCode) {
this.city = city;
this.street = street;
this.zipcode = zipCode;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getZipCode() {
return zipcode;
}
public void setZipCode(String zipCode) {
this.zipcode = zipCode;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Address address = (Address) o;
return Objects.equals(city, address.city) &&
Objects.equals(street, address.street) &&
Objects.equals(zipcode, address.zipcode);
}
@Override
public int hashCode() {
return Objects.hash(city, street, zipcode);
}
}
equals, hashcode 의 사용이유와 예제
https://vprog1215.tistory.com/204?category=989394
참고: https://www.inflearn.com/course/ORM-JPA-Basic/lecture/21713?tab=curriculum
728x90
반응형
'DataBase > JPA' 카테고리의 다른 글
[JPA] JPQL 이란? (0) | 2021.09.23 |
---|---|
[JPA] 값타입 컬렉션 (0) | 2021.09.17 |
[JPA] CASCADE (0) | 2021.08.22 |
[JPA] Lazy 와 Eager (0) | 2021.08.19 |
[JPA] 프록시 (0) | 2021.08.11 |
댓글