기본타입과 참조타입

int a = 10;
int b = a;

System.out.println(a); // 10
System.out.println(b); // 10

기본타입은 항상 값을 복사한다.
a와 b는 다른 저장공간을 가진다.

Integer a = 10;
Integer b = a;

a.setValue(20); // (실제로 없는 메서드)

System.out.println(a); // 20
System.out.println(b); // 20

래퍼클래스는 주소가 공유가 된다.
따라서 값이 바뀐다면 같은 주소를 참조하고 있기 때문에 바뀐 값이 공유되어 출력된다.
하지만 래퍼클래스(참조형)은 불변객체이므로, 값이 바뀔 수가 없다!


임베디드 타입

  • JPA에서 여러 필드를 하나의 클래스로 묶어서 엔티티에 포함시키는 방법
  • 엔티티가 아닌 값 객체(Value Object)이며, 별도의 테이블이 생성되지 않는다.
  • String city, String street, String zipcode처럼 흩어져 있던 필드를 Address라는 클래스로 묶어서 재사용할 수 있다.
  • 기본 생성자가 필수이다!
@Embeddable
@NoArgsConstructor
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; // ✅ 임베디드 타입
}

❓ 임베디드 타입... 써야할까요?

  • 프로젝트가 커졌을 때 의미로 묶었을 경우, 관련 필드 메서드를 활용할 수 있다.
  • 여러 엔티티에서 해당 임베디드 타입을 재사용할 수 있다.
  • 관련된 필드가 하나로 묶이니, 응집도가 증가한다.
    → 즉, 관련된 것들끼리 하나의 목적으로 잘 뭉쳐져 있는 것을 뜻한다.

❗️ 임베디드 타입 주의할 점!

JPA에서 임베디드 타입(Embeddable)을 공유할 때 생길 수 있는 부작용(side effect)은 주로 참조에 의한 변경(shared reference mutation)에서 발생한다.

임베디드 타입은 참조 타입이라, 여러 엔티티가 같은 임베디드 객체를 참조하면 한 곳의 변경이 다른 곳에도 영향을 줄 수 있다!

임베디드 타입 클래스 (Address)

@Embeddable
public class Address {
    private String city;
    private String street;

    public Address(String city, String street) {
        this.city = city;
        this.street = street;
    }

    public void setCity(String city) {
        this.city = city;
    }
}

테스트 코드

Address address = new Address("Seoul", "Gangnam");

Member member1 = new Member("UserA", address); // 1차 공유
Member member2 = new Member("UserB", address); // 2차 공유

member1.getAddress().setCity("Busan"); // member1만 바꿨는데...
  • member1이 주소를 "Busan"으로 바꾸면,
    member2의 주소도 함께 "Busan"이 된다.
  • 두 명의 회원이 같은 주소 인스턴스를 참조하고 있기 때문이다.

✅ 해결방법

@Embeddable
@Getter // Getter만 설정
@NoArgsConstructor
@AllArgsConstructor // 필드 생성자
public class Address {
    private String city;
    private String street;
}

💡 Java에서 final 필드는 JPA에서 직접 지원하지 않으므로, 필드에 final을 쓰지 않고 setter만 제거하는 방식으로 한다.

  • 불변객체로 설계해야 한다!
  • 따라서 생성자로만 값을 설정할 수 있게 하고, Setter를 만들지 말자.
  • 값을 바꾸고 싶다면 객체를 통으로 새로 만들어 갈아끼는게 좋다고 한다.

김영한 자바 ORM 표준 JPA 프로그래밍

profile
wannabe---ing

0개의 댓글

Powered by GraphCDN, the GraphQL CDN