오늘 하루에 집중하자
  • [JPA] 3. 매핑 종류(공사중..🚧)
    2025년 01월 11일 23시 26분 41초에 업로드 된 글입니다.
    작성자: nickhealthy

    JPA에서 엔티티 매핑(Entity Mapping)은 객체 지향 프로그래밍에서 객체와 데이터베이스의 테이블 간의 관계를 정의하는 중요한 개념이다. JPA는 자바 객체와 관계형 데이터베이스의 테이블 간의 매핑을 통해 객체-관계 매핑(ORM)을 구현한다.
     

    객체와 테이블 매핑


    다음과 같은 개념이 존재한다. 차례대로 자세히 알아보자.

    • 객체와 테이블 매핑: `@Entity`, `@Table`
    • 필드와 컬럼 매핑: `@Column`
    • 기본 키 매핑: `@Id`
    • 연관관계 매핑: `@OneToOne`, `@OneToMany / @ManyToOne` , `@ManyToMany`, `@JoinColumn`

     

    @Entity

    • `@Entity` 붙은 클래스는 JPA가 관리, 해당 클래스가 엔티티임을 명시한다.
    • 엔티티는 데이터베이스의 테이블과 매핑되는 자바 클래스를 의미하며, 필수 어노테이션이다.

     

    [주의사항]

    • 기본 생성자 필수(파라미터가 없는 `public` 또는 `protected` 생성자)
      • JPA는 내부적으로 리플렉션을 사용하기 때문에 기본 생성자가 필요하다.
    • final 클래스, enum, interface, inner 클래스 사용 X
    • 저장할 필드에 final 사용 X
    @Entity
    public class Employee {
        @Id
        private Long id;
        
        private String name;
        
        private String department;
        
        // Getters and Setters
    }

     

    @Table

    • JPA에서 엔티티 클래스와 데이터베이스 테이블 간의 매핑 정보를 제공하는 어노테이션이다.
    • 기본적으로, JPA는 엔티티 클래스의 이름을 테이블 이름으로 사용하지만, `@Table`을 사용하여 매핑할 테이블 이름이나 기타 속성(스키마, 고유 제약 조건 등)을 명시적으로 설정할 수 있다.

     

    @Table 어노테이션의 주요 속성

    1. name:
      • 이 속성은 엔티티 클래스와 매핑할 데이터베이스 테이블의 이름을 지정한다. 기본적으로 클래스 이름과 같은 테이블을 사용하지만, @Table(name = "테이블_이름")을 사용하여 다른 테이블 이름을 지정할 수 있다.
      @Entity
      @Table(name = "employee_table")
      public class Employee {
          @Id
          private Long id;
          
          private String name;
          
          private String department;
          
          // Getters and Setters
      }
      
      위 예시에서 Employee 엔티티는 데이터베이스에서 employee_table이라는 이름의 테이블에 매핑된다.

    2. schema:
      • 이 속성은 테이블이 속할 스키마를 지정한다. 데이터베이스에서 여러 스키마를 사용하는 경우, 이 속성을 통해 특정 스키마에 테이블을 매핑할 수 있다.
      @Entity
      @Table(name = "employee_table", schema = "hr")
      public class Employee {
          @Id
          private Long id;
          
          private String name;
          
          private String department;
          
          // Getters and Setters
      }
      
      위 예시에서는 employee_table이 hr라는 스키마에 매핑된다.

    3. catalog:
      • 이 속성은 테이블이 속할 카탈로그를 지정한다. 카탈로그는 데이터베이스의 논리적인 그룹을 나타내며, 주로 DBMS에서 여러 데이터베이스를 구분하는 데 사용된다. 이 속성은 보통 MySQL, Oracle과 같은 DBMS에서 사용된다.
      @Entity
      @Table(name = "employee_table", catalog = "company_db")
      public class Employee {
          @Id
          private Long id;
          
          private String name;
          
          private String department;
          
          // Getters and Setters
      }
      
      여기서는 employee_table이 company_db라는 카탈로그에 속하게 된다.

    4. uniqueConstraints:
      • 이 속성은 테이블에 고유 제약 조건을 추가할 수 있습니다. 예를 들어, 여러 컬럼에 대해 고유 제약 조건을 설정하려면 이 속성을 사용한다.
      @Entity
      @Table(
          name = "employee_table",
          uniqueConstraints = @UniqueConstraint(columnNames = {"name", "department"})
      )
      public class Employee {
          @Id
          private Long id;
          
          private String name;
          
          private String department;
          
          // Getters and Setters
      }
      
      이 예시에서는 name과 department 컬럼 조합에 대해 고유 제약 조건이 설정된다.

    필드와 컬럼 매핑


    어노테이션 설명
    @Column 필드와 컬럼 매핑
    @Temporal 날짜 타입 매핑
    @Enumerated Enum 타입 매핑

    - EnumType.ORDINAL: Enum 순서를 데이터베이스에 저장(0, 1..)
    - EnumType.STRING: Enum 이름을 데이터베이스에 저장

    항상 EnumType.STRING 사용해야함
    @Lob BLOB, CLOB 매핑
    @Transient 특정 필드를 컬럼에 매핑하지 않음(매핑 무시)

     

    @Column

    • 엔티티 클래스의 필드는 데이터베이스 테이블의 컬럼과 매핑된다.
    • 기본적으로 필드 이름이 컬럼 이름과 일치하지만, `@Column` 어노테이션을 사용하여 매핑 정보를 세부적으로 설정할 수 있다.
    @Entity
    public class Employee {
        @Id
        private Long id;
    
        @Column(name = "emp_name", nullable = false)
        private String name;
    
    // Getters and Setters
    }

    여기서 `@Column(name = "emp_name")`는 name 필드를 `emp_name`이라는 컬럼에 매핑하고, `nullable = false`는 해당 컬럼이 NULL 값을 허용하지 않는 제약조건을 추가한 것이다.

     

    @Temporal

    • 날짜 타입을 매핑할 때 사용한다.
    • 참고로 최근 버전에서는 `LocalDate`, `LocalDateTime` 타입을 사용하게 되면 해당 어노테이션을 사용하지 않아도 날짜 타입으로 설정된다.

     

    @Enumerated

    • Enum 타입을 매핑할 때 사용한다.
    • Enum 타입을 사용하면 기본 값이 `EnumType.ORDINAL`이기 때문에 기본적으로 데이터베이스에서는 Integer 타입으로 설정된다.
    • Enum 타입을 사용할 땐 `EnumType.STRING`으로 항상 설정해서 사용해야 한다.

     

    @Lob

    • Lob에는 지정할 수 있는 속성이 없다.
    • 매핑하는 필드 타입이 문자면 CLOB 매핑, 나머지는 BLOB으로 매핑한다.
      • CLOB: String, char[], java.sql.CLOB
      • BLOB: byte[], java.sql.BLOB

     

    @Transient

    • 특정 필드를 데이터베이스 컬럼에 매핑하지 않는다.
    • 주로 메모리 상에서만 임시로 어떤 값을 보관하고 싶을 때 사용한다.

     

    🔹 예제 코드

    현재 엔티티를 기준으로 어떻게 데이터베이스가 생성되는지 확인해보자.
    물론 운영 환경에서는 직접 쿼리 스크립트를 작성하여 데이터베이스를 생성해야 한다.

    package hellojpa;
    
    import jakarta.persistence.*;
    
    import java.util.Date;
    
    // JPA가 관리할 객체
    @Entity
    public class Member {
    
        @Id // 데이터베이스 PK와 매핑
        private Long id;
    
        // DDL 생성 기능
        @Column(unique=true, length = 10)
        private String name;
    
        private Integer age;
    
        @Enumerated(EnumType.STRING)
        private RoleType roleType;
    
        @Temporal(TemporalType.TIMESTAMP)
        private Date createdDate;
    
        @Temporal(TemporalType.TIMESTAMP)
        private Date lastModifiedDate;
    
    	@Lob
        private String description;
    
    
    }

     

    🔹 결과

    현재 H2 데이터베이스 기준으로 다음과 같이 생성되었다. 위에서 설명한대로 모두 정상적으로 데이터 타입이 지정된 것을 확인할 수 있다.

    결과

     

    기본 키 매핑


     

     

    연관관계 매핑


    JPA에서는 엔티티 간의 관계를 매핑할 수 있다. 관계 매핑은 크게 1:11:NN:1N:M 관계로 나눌 수 있다.

     

    @OneToOne

    두 엔티티가 1:1 관계일 때 사용한다. 예를 들어, 한 사람은 하나의 여권만 가질 수 있을 때 사용된다.

    @Entity
    public class Person {
        @Id
        private Long id;
    
        @OneToOne
        @JoinColumn(name = "passport_id")
        private Passport passport;
    
    // Getters and Setters
    }

     

    @OneToMany / @ManyToOne

    `@OneToMany`와 `@ManyToOne`은 1:N, N:1 관계를 매핑할 때 사용한다. 예를 들어, 한 부서에 여러 직원이 있을 수 있고, 여러 직원은 한 부서에 속할 수 있다.

    @Entity
    public class Department {
        @Id
        private Long id;
    
        @OneToMany(mappedBy = "department")
        private List<Employee> employees;
    
    // Getters and Setters
    }
    
    @Entity
    public class Employee {
        @Id
        private Long id;
    
        @ManyToOne
        @JoinColumn(name = "department_id")
        private Department department;
    
    // Getters and Setters
    }

     

     

    @ManyToMany

    `@ManyToMany`는 N:M 관계를 매핑할 때 사용한다. 예를 들어, 여러 학생이 여러 과목을 들을 수 있을 때 사용된다.

    @Entity
    public class Student {
        @Id
        private Long id;
    
        @ManyToMany
        @JoinTable(name = "student_subject",
                   joinColumns = @JoinColumn(name = "student_id"),
                   inverseJoinColumns = @JoinColumn(name = "subject_id"))
        private List<Subject> subjects;
    
    // Getters and Setters
    }

     

    @JoinColumn

     

     

     

     

    데이터베이스 스키마 자동 생성


    하이버네이트 설정 값(`hibernate.hbm2ddl.auto`)에 따라 DDL(Data Definition Language)을 애플리케이션 실행 시점에 자동 생성할지 결정한다.

    • 운영 환경에서는 절대 `create`, `create-drop`, `update` 옵션을 사용하면 안된다.
      • create, create-drop: 운영 데이터가 모두 날라간다.
      • update: 테이블 수정이 이뤄지면 테이블 전체에 락이 발생할 수 있다. 락 발생 시 서비스 장애로 이어진다.
    • 개발 초기 단계는 `create` 또는 `update` 사용
    • 스테이징과 운영 서버는 `validate` 또는 `none` 사용

     

    설정 값 목록

    옵션 설명
    create 기존 테이블 삭제 후 다시 생성(DROP + CREATE)
    create-drop create와 같으나 종료 시점에 테이블 DROP
    update 변경분만 반영
    validate 엔티티와 테이블이 정상 매핑되었는지만 확인
    none 사용하지 않음

     
     

    DDL 생성 기능

    아래와 같은 제약 조건을 엔티티에서 설정할 수 있는데 이것을 'DDL 생성 기능'이라고 한다.
    DDL 생성 기능은 DDL을 자동 생성할 때만 사용되고, JPA의 실행 로직(런타임)에는 영향을 주지 않는다.
     

    🔹 예제 코드

    DDL 생성 기능을 통해 `name` 컬럼의 제약 조건을 추가했다.

    package hellojpa;
    
    import jakarta.persistence.Column;
    import jakarta.persistence.Entity;
    import jakarta.persistence.Id;
    
    // JPA가 관리할 객체
    @Entity
    public class Member {
    
        @Id // 데이터베이스 PK와 매핑
        private Long id;
    	
        // DDL 생성 기능
        @Column(unique=true, length = 10)
        private String name;
       
    }

     

    🔹 결과

    테이블을 생성할 때 제약조건도 추가되는 것을 확인할 수 있다.
    하이버네이트 설정 값(`hibernate.hbm2ddl.auto`)이 `create`가 아닌 `update` 옵션도 마찬가지로 제약조건이 추가된다.

    결과

     

    댓글