관리 메뉴

개발노트

JPA Annotation 정리 본문

API/JPA

JPA Annotation 정리

YoonGwon 2021. 1. 18. 17:06

 JPA를 사용하는데 가장 중요한 일은 엔티티와 테이블을 정확히 매핑하는 것이다.

따라서 매필 어노테이션을 숙지하고 사용해야 한다. JPA는 다양한 매핑 어노테이션을 지원하는데 크게 4가지로 분류할 수 있다.
오른쪽에는 대표 어노테이션들을 적어보았다.

  • 객체와 테이블 매핑 : @Entity, @Table
  • 기본 키 매핑 : @Id
  • 필드와 컬럼 매핑 : @Column
  • 연관관계 매핑 : @ManyToOne, @Joincolumn

객체와 테이블 매핑, 기본 키 매핑, 필드와 컬럼 매핑에 대해 알아보고 연관관계 매핑은 다음 포스팅에서 이야기해보도록 하겠다.

 

 

 

@Entity

- @Entity 어노테이션은 데이타베이스의 테이블과 1:1로 매칭되는 객체 단위이며 Entity 객체의 인스턴스 하나가 테이블에서 하나의 레코드 값을 의미하며 테이블과 매핑할 클래스필수로 붙여줘야한다. 그래서 객체의 인스턴스를 구분하기 위한 유일한 키값을 가지는데 이것은 테이블 상의 Primary Key 와 같은 의미를 가지며 @I어노테이션으로 표기된다.

 

속성 기능 기본값
name JPA에서 사용할 엔티티 이름을 지정한다. 보통 기본값인 클래스 이름을 사용한다. 만약 다른 패키지에 이름이 같은 엔티티 클래스가 있다면 이름을 지정해서 충동하지 않도록 해야 한다. 설정하지 않으면 클래스 이름을 그대로 사용한다. CamelCase 유지
(예 : Member)
@Entity
@Table(name = "Member")
public class Member {
     ...
}


@Entity
public class MemberA { 
    ...
}

- @Entity 적용 시 주의사항은 다음과 같다.

  • 기본 생성자는 필수다(파라미터가 없는 public 또는 protected 생성자).
  • final 클래스, enum, interface, inner 클래스에는 사용할 수 없다.
  • 저장할 필드에 final을 사용하면 안된다.

@Table

- @Table은 엔티티와 매핑할 테이블을 지정한다. 생략하면 매핑한 엔티티 이름을 테이블 이름으로 사용한다.

 

속성 기능 기본값
name 매핑할 테이블 이름 엔티티 이름을 사용한다.
catalog catalog 기능이 있는 데이터베이스에서 catalog를 매핑한다.  
schema schema 기능이 있는 데이터베이스에서 schema를 매핑한다.  
uniqueConstraints(DDL) DDL 생성시에 유니크 제약조건을 만든다. 2개 이상의 복합 유니크 제약조건도 만들 수 있다. 참고로 이 기능은 스키마 자동 생성 기능을 사용해서 DDL을 만들 때만 사용된다.  
@Entity
@Table(name = "Member")
public class Member {
     ...
}


@Entity
public class MemberA { 
    ...
}

 


@Column

- @Column 어노테이션은 데이타베이스의 테이블에 있는 컬럼과 동일하게 1:1로 매칭되기 때문에 Entity 클래스안에 내부변수로 정의 된다.만약 테이블에 a, b, c 컬럼이 있다면 각각 3개의 @Column 어노테이션을 작성 하게 된다. 다만 이때 의도적으로 필요없는 컬럼들은 장성하지 않아도 되는데 데이타베이스 테이블에 실제 a, b, c, d 총 4개의 컬럼이 있더라도 a, b, c 컬럼만 Entity 클래스에 작성해도 무방 하다는 이야기이다.

 

- 이때 @Column 어노테이션은 별다른 옵션을 설정하지 않는다면 생략이 가능하다. 즉 Entity 클래스에 정의된 모든 내부변수는 기본적으로 @Column 어노테이션을 포함한다고 볼 수 있다.

 

Spring Boot 의 spring.jpa.hibernate.ddl-auto 설정이 create 혹은 update 로 되어 있을 경우 create 일때는 최초에 한번 컬럼이 생성이 되고, update 일때는 Entity클래스에 있지만 해당 테이블에 존재하지 않는 컬럼을 추가로 생성해 준다. 하지만 컬럼의 데이타 타입이 변경 되었거나 길이가 변경 되었을때 자동으로 데이터베이스에 반영을 해주지는 않기 때문에 속성이 변경되면 기존 테이블을 dropcreate 하던지 개별로 alter table 을 통해 직접 ddl 구문을 적용하는 것이 좋다.

 

spring.jpa.hibernate.ddl-auto 설정이 create-drop 으로 되어 있으면 프로젝트가 시작될때 자동으로 기존 테이블을 drop 한 후 create 를 해준다. 하지만 기존 스키마가 전부 삭제 되기 때문에 시스템 설계와 개발 시점에만 사용해야 하며 운영 시점에 create, update, create-drop 을 사용하지 않아야한다.

 

@Column 도 @Entity 어노테이션과 동일하게 name 속성을 명시하지 않으면 Entity 클래스에 정의한 컬럼 변수의 이름으로 생성이 된다. 그렇기 때문에 CamelCase 로 작성된 컬럼 변수가 있다면 UnderScore 형식으로 name 을 명시적으로 작성 한다.

 

- 데이터베이스상에서 컬럼은 실제 데이터가 가질 수 있는 최대 길이를 가지게 되는데 이것은 데이터베이스에 데이터를 효율적으로 관리하기 위해서 이다. @Column 에도 이처럼 length 속성으로 길이를 명시 할 수 있다. 만약 length 속성이 없다면 기본 길이인 255가 지정 된다. 이것은 문자열 형태인 데이터 속성에만 해당 되며 큰 숫자를 표현하는 BigDecimal 일 결우 precision, scale 로 최대 길이를 지정 할 수 있다.

 

 

@Column
private String code;

@Column(length = 100)
private String name;

//@Column 은 생략이 가능하다.
private String desctiption;

@Column(precision = 11, scale = 2)
private BigDecimal amount;

@Column
private Integer size;

@Column(name = "register_date")
private LocalDateTime registerDate;

 

*hibernate.hbm2ddl.auto 속성 참고

옵션 설명
create 기존 테이블을 삭제하고 새로 생성한다. DROP + CREATE
create-drop create 속성에 추가로 애플리케이션을 종료할 때 생성한 DDL을 제거한다.
DROP + CREATE + DROP
update 데이터베이스 테이블과 엔티티 매핑정보를 비교해서 변경 사항만 수정한다.
validate 데이터베이스 테이블과 엔티티 매핑정보를 비교해서 차이가 있으면 경고를 남기고 애플리케이션을 실행하지 않는다. 이설정은 DDL을 수정하지 않는다.
none 자동 생성 기능을 사용하지 않으려면 hibernate.hdm2ddl.auto 속성 자체를 삭제하거나 유효하지 않은 옵션 값을 주면 된다.
(참고로 none은 유효하지 않은 옵션 값이다)

@Id

- 데이터베이스의 테이블은 기본적으로 유일한 값을 가진다. 그것을 PK (Primary Key) 라고 하는데 데이터베이스는 이 유일한 키값을 기준으로 질의한 데이터를 추출해 결과셋으로 반환해 준다. 테이블 상에 PK 가 없는 테이블도 있지만 대부분의 경우 반드시 PK 가 존재한다.

 

JPA 에서도 Entity 클래스 상에 해당 PK 를 명시적으로 표시를 해야 되는데 그것을 @Id 어노테이션을 이용해 이것이 PK 임을 지정 한다.

 

- 만약 Spring Boot 의 spring.jpa.hibernate.ddl-auto 속성이 create 로 되어 있고 아직 해당 테이블이 데이터베이스 상에 존재하지 않는다면 EntityManager 가 DDL 을 통해 테이블을 생성하면서 PK 를 같이 생성해 준다.

 

@id
@Column
private String code;

 


@GeneratedValue

- PK 컬럼의 데이타 형식은 정해져 있지는 않으나 구분이 가능한 유일한 값을 가지고 있어야 하고 데이터 경합으로 인해 발생되는 데드락 같은 현상을 방지 하기 위해 대부분 BigInteger 즉 Java 의 Long 을 주로 사용합니다.

 

- 물론 String 형태의 고정된 키값을 직접 생성해서 관리하기도 한다. 중요한 것은 대량의 요청이 유입 되더라도 중복과 deadlock 데디락이 발생 되지 않을 만큼 키값이 빨리 생성이 되고 안전하게 관리 되어야 한다는 점이다.

 

*deadlock 이란?

 동일한 시점에 요청이 유입 되었을대 데이터베이스는 테이블 혹은 레코드를 lock 을 걸어 데이터가 변경되지 않도록 막아 놓고 다른 작업을 진행한다.

 

 이때 1번째 요청이 A 테이블의 값을 변경하고 lock 을 걸어둔 상태에서 B 테이블을 사용하려고 하고, 2번째 요청이 B 테이블의 값을 변경하고 lock 을 걸어둔 상태에서 A 테이블을 사용하려고 할때 데이터베이스는 우선순위를 판단 할 수 없어 그대로 교착상태에 빠져 버린다.

 

이때는 어쩔 수 없이 강제로 시스템을 재시작하여 데이터베이스 커넥션을 초기화 시켜줘야 한다.

 

- 가장 보편적으로 사용이 되는 데이터베이스인 Mysql, Oracle 에는 Long 타입의 키값을 생성하는 방식이 서로 조금 다른데, Mysql은 auto increment 방식을 Oracle 은 sequence 방식을 사용한다.

 

* auto increment
 먼저 Mysql 의 auto increment 방식은 숫자형의 PK 컬럼 속성을 auto increment 로 지정하면 자동으로 새로운 레코드가 생성이 될때마다 마지막 PK 값에서 자동으로 +1 을 해주는 방식이다. 이를 위해 @GenerateValue 어노테이션의 stragegy 속성을 GenerationType.IDENTITY 로 지정해 auto increment 컬럼인 것을 EntityManager 에 알려 준다.


 이때 자동으로 생성되는 값을 가지는 PK 컬럼의 이름은 명시적으로 id 로 지정하는 것이 관례이다.
@id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
* sequence
Oracle 에서 사용되는 sequence 방식은 sequence Oracle 객체를 생성해 두고 해당 sequence 를 호출할때마다 기존 값의 +1 이 된 값을 반환해 주는 방식이다. 이를 위해 @GeneratedValue 어노테이션의 strategy 속성을 GenerationType.SEQUENCE 로 지정해 sequence 를 사용해 PK 값을 사용하겠다고 지정한다.

 

@Id
@SequenceGenerator(name="seq", sequenceName="jpa_sequence")
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="seq")
private Long id;

 

@EmbeddedId

- 앞서 데이터베이스의 테이블은 기본적으로 유일한 값을 가지는데 그것을 PK 라고 이야기 한다고 설명하였다. 일반적인 경우에는 PK 를 단일 @Id 로 구성하지만 경우에 따라선 복합키로서 테이블의 PK 를 정의 하기도 한다. 복합키는 두개 이상의 @Id 로 구성이 되는데 이를 직접 Entity 에 정의하는 것이 아니라 별도의 Value 를 사용해 복합키를 정의한다.

 

- 먼저 Value 를 생성한 다음 @Embeddable 어노테이션을 이용해 이 Value 가  Entity 에 삽입이 가능함을 명시 하고 Entity 에서는 @EmbeddedId 어노테이션을 이용해 이 Entity 에 해당 Value 를 PK 로 사용한다고 지정한다.

@Embeddable
public class CompanyOrganizationKey implements Serializable {
	@Column(name = "company_code")
    private String companyCode;
    
    @Column(name = "organization_code")
    private String organizationCode;
}

@Entity(name = "company_organization")
public class CompanyOrganization {
	@EmbeddedId
    protected CompanyOrganizationKey companyOrganizationKey;
}

참조 : www.icatpark.com/entry/JPA-%EA%B8%B0%EB%B3%B8-Annotation-%EC%A0%95%EB%A6%AC

728x90