Database/JPA / / 2024. 9. 19. 12:43

JPA 값 타입으로 객체 모델링을 유연하게 설계하는 방법

JPA에서의 값 타입은 데이터베이스의 단일 값이나 복합 값을 표현하는 타입으로, 엔티티와는 다른 특성을 가지고 있어. 값 타입은 주로 엔티티의 속성(필드)을 정의할 때 사용되며, 객체의 생명 주기를 엔티티에 종속시킨다는 특징이 있어. JPA에서 값 타입은 크게 기본값 타입임베디드(복합) 값 타입으로 나눌 수 있어.

 

1. 기본값 타입

 

기본값 타입은 자바의 기본 데이터 타입(int, double, String 등)과 JPA가 제공하는 타입을 말해. 이 값들은 데이터베이스 테이블에서 하나의 컬럼에 대응하고, 보통 복사(by value) 방식으로 처리돼. 기본 타입은 엔티티와 달리 생명 주기를 따로 관리하지 않고, 엔티티에 종속돼 있어.

 

예시:

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name; // 기본값 타입
    private int age;     // 기본값 타입
}

nameage는 기본값 타입이며, 각각 데이터베이스의 VARCHAR, INTEGER 같은 컬럼에 매핑돼.

 

2. 임베디드(복합) 값 타입

 

임베디드 값 타입은 여러 필드를 하나의 객체로 묶어서 사용할 수 있는 값 타입이야. 주로 비즈니스 의미가 있는 값들을 그룹화해서 객체로 만들고, 엔티티의 속성으로 사용하는 방식이지. @Embeddable@Embedded 어노테이션을 통해 값 타입을 정의하고 사용할 수 있어.

 

예시:

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

    // Getters and Setters
}

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Embedded
    private Address address; // 임베디드 값 타입
}

Address는 임베디드 값 타입으로, User 엔티티에 포함된 객체야. 이 값 타입은 User 엔티티가 저장되거나 삭제될 때 같이 처리돼.

 

3. 컬렉션 값 타입

 

컬렉션 값 타입은 값 타입을 컬렉션 형태로 저장하는 방식이야. 엔티티에 속한 값 타입 컬렉션은 별도의 테이블에 저장돼 관리되며, JPA에서는 이를 지원하기 위해 @ElementCollection을 사용해.

 

예시:

@Embeddable
public class PhoneNumber {
    private String countryCode;
    private String number;

    // Getters and Setters
}

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ElementCollection
    private List<PhoneNumber> phoneNumbers; // 값 타입 컬렉션
}

phoneNumbers 필드는 여러 개의 PhoneNumber 객체를 컬렉션으로 관리하는 값 타입이야.

 

특징:

 

독립성 부족: 값 타입은 엔티티에 종속적이기 때문에, 값 타입만 따로 저장하거나 조회할 수 없어.

불변성: 값 타입은 기본적으로 변경 불가능(immutable)하게 설계하는 것이 권장돼. 값이 변경되면 새롭게 값을 교체하는 방식으로 처리해야 해.

참조 무결성 없음: 값 타입은 별도의 식별자가 없고 엔티티와 함께 관리되므로, 값 타입 객체를 공유하거나 참조할 수 없어.

 

정리

 

기본값 타입: 단일 값 (예: String, int)을 저장하는 타입.

임베디드 값 타입: 여러 필드를 가진 객체를 엔티티의 일부로 사용하는 복합 타입.

컬렉션 값 타입: 값 타입을 컬렉션 형태로 관리.

 

JPA에서 값 타입은 객체 모델을 더 유연하게 설계하고, 데이터를 그룹화해서 사용할 수 있게 도와주는 중요한 기능이야.

 

@AttributeOverride는 JPA에서 임베디드 값 타입이나 상속 구조에서 기본적으로 매핑된 필드나 속성의 매핑 정보를 재정의할 때 사용하는 어노테이션이야. 특히, 같은 값 타입을 여러 엔티티에서 사용할 때 각 필드의 매핑 정보를 변경할 필요가 있을 때 유용해.

 

주로 사용하는 상황:

 

1. 임베디드 값 타입에서 속성 이름 변경: 여러 엔티티에서 동일한 값 타입을 사용하지만, 해당 필드가 다른 데이터베이스 컬럼에 매핑되어야 할 때 사용해.

2. 상속 관계에서 필드 재정의: 상속받은 엔티티에서 부모 클래스의 속성 매핑을 재정의할 때도 사용할 수 있어.

 

기본 사용법:

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

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @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 = "office_city")),
        @AttributeOverride(name = "street", column = @Column(name = "office_street")),
        @AttributeOverride(name = "zipcode", column = @Column(name = "office_zipcode"))
    })
    private Address officeAddress;
}

 

설명:

 

@Embedded: 임베디드 타입을 선언할 때 사용.

@AttributeOverrides: 여러 속성의 매핑 정보를 재정의할 때 사용하며, 그 안에 @AttributeOverride를 포함해.

@AttributeOverride(name, column): 재정의할 속성의 이름(name)과 그 속성이 매핑될 새로운 컬럼 정보(column)를 지정.

 

위 예시에서 Address라는 임베디드 값 타입을 homeAddressofficeAddress로 각각 다른 컬럼에 매핑하고 있어. homeAddresshome_city, home_street, home_zipcode 컬럼에, officeAddressoffice_city, office_street, office_zipcode 컬럼에 매핑돼.

 

정리:

 

@AttributeOverride같은 값 타입이나 상속된 필드의 기본 매핑을 재정의하고 싶을 때 사용하는 어노테이션이야.

주로 임베디드 값 타입을 사용할 때 각각의 필드를 서로 다른 컬럼에 매핑할 수 있도록 해줘.

 

Tip💡

**쉘로우 카피(Shallow Copy)**와 **딥 카피(Deep Copy)**는 객체 복사 시 복사의 깊이를 의미하는 개념이야. 두 방식은 객체 내부의 참조 자료형(예: 배열, 객체)이 어떻게 복사되는지에 따라 다르지.

 

1. 쉘로우 카피 (Shallow Copy)

 

얕은 복사라고도 해.

객체의 최상위 필드만 복사하고, 그 필드가 참조하고 있는 객체는 복사하지 않아. 즉, 복사된 객체와 원본 객체가 같은 참조를 가리키게 돼.

참조형 데이터(예: 배열, 객체)는 복사하지 않고 참조 주소만 복사하기 때문에, 복사본에서 참조형 데이터를 변경하면 원본에도 영향을 줄 수 있어.

 

예시:

public class Main {
    public static void main(String[] args) {
        int[] original = {1, 2, 3};
        int[] shallowCopy = original;

        shallowCopy[0] = 100;  // 원본 배열도 영향을 받음
        System.out.println(original[0]);  // 100 출력
    }
}

 

2. 딥 카피 (Deep Copy)

 

깊은 복사는 객체의 모든 필드와 그 필드가 참조하는 객체들까지 모두 복사하는 방식이야.

즉, 복사된 객체가 원본 객체와 완전히 독립적으로 동작해. 원본과 복사본이 각각의 참조를 가지기 때문에, 복사본에서 참조형 데이터를 변경해도 원본에 영향을 미치지 않아.

 

예시:

public class Main {
    public static void main(String[] args) {
        int[] original = {1, 2, 3};
        int[] deepCopy = original.clone();  // 배열을 완전히 복사

        deepCopy[0] = 100;  // 원본 배열에는 영향 없음
        System.out.println(original[0]);  // 1 출력
    }
}

 

차이점 정리:

 

쉘로우 카피: 참조만 복사, 변경 시 원본에 영향.

딥 카피: 모든 객체를 독립적으로 복사, 변경 시 원본에 영향 없음.

 

쉘로우 카피는 성능이 좋지만 참조 문제로 의도치 않은 부작용이 생길 수 있고, 딥 카피는 성능은 조금 더 떨어질 수 있지만 안전한 복사 방법이야.

 

Tip💡

Q. Integer, String 등은 자바가 제공하는 대표적인 불변 객체인가?

A. 네, **Integer**와 **String**은 자바에서 제공하는 대표적인 **불변 객체(immutable object)**야.

 

1. String 클래스

 

불변 객체: String은 생성된 후 값을 변경할 수 없는 불변 객체로 설계되었어. 문자열을 조작하려고 하면 새로운 String 객체가 생성되고, 기존 객체는 변하지 않아.

불변성 이유: 메모리 효율성, 보안, 스레드 안전성 등을 위해 설계되었어. 여러 곳에서 같은 문자열 리터럴을 공유할 수 있고, 그 값이 변경되지 않으므로 프로그램 안정성을 높여줘.

 

예시:

String str = "hello";
str = str + " world";  // 새로운 String 객체 생성

 

이 경우 str은 처음에 "hello"를 가리키고 있었지만, str + " world"로 새로운 문자열이 만들어지고 str은 이제 그 새로운 객체를 가리켜.

 

2. Integer 클래스

 

불변 객체: Integer는 자바의 래퍼 클래스 중 하나로, int와 같은 기본형 타입을 객체로 다루기 위해 사용돼. Integer 객체는 생성 후 변경되지 않아. 따라서, 숫자를 변경하려면 새로운 Integer 객체를 생성해야 해.

불변성 이유: 래퍼 클래스들은 기본형 타입의 값을 감싸는 단순한 역할을 하기 때문에 값이 변경될 필요가 없어. 이를 통해 메모리 관리와 성능이 더 최적화될 수 있어.

 

예시:

Integer a = 100;
Integer b = a;
a = 200;

이 코드에서 a가 처음에 100을 가리켰다가 200으로 변경되지만, 이는 새로운 Integer 객체를 할당하는 것이지 기존의 100 값을 변경하는 것은 아니야. b는 여전히 100을 가리키고 있어.

 

불변 객체의 장점:

 

안정성: 여러 스레드에서 동시에 접근해도 안전해.

캐싱 가능: 동일한 값에 대한 객체를 재사용함으로써 메모리 효율성을 높일 수 있어. (예: 자바의 Integer 캐싱)

 

결론:

 

**String**과 **Integer**는 자바에서 제공하는 대표적인 불변 객체.

불변성 덕분에 성능 및 메모리 관리가 더 효율적이며, 스레드 안전성을 보장할 수 있어.

 

 

  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유