JPA 엔티티와 매핑 관계
객체와 테이블 매핑 : @Entity, @Table
기본 키 매핑 : @Id
필드와 컬럼 매핑 : @Column
연관관계 매핑 : @ManyToOne, @JoinColumn
@ Entity
-
데이터베이스의 테이블과 일대일로 매칭되는 객체 단위를 의미한다.
-
Entity 객체의 인스턴스 하나가 테이블에서 하나의 레코드 값을 의미한다.
-
따라서 인스턴스를 구분하기 위해 PK를 가지는데 이것은 @id로 정의를 한다.
-
Spring Boot를 설정 시 spring.jp.hibernate.ddl-auto 설정이 create 또는 update로 되어 있는 경우
EntityManager가 자동으로 DDL(Data Definition Language)를 수행해 테이블을 생성해 준다. -
이때 명시적으로 @Table의 name속성을 이용해 데이터베이스상의 실제 테이블 명칭을 지정하지 않는다면, Entitiy 클래스의 이름 그대로 CamelCase를 유지하여 테이블이 생성된다.
-
따라서 Table이름을 명시적으로 작성하는 것이 관례이다.
-
데이터베이스에서 보편적으로 사용하는 명명법은 UnderScore가 원칙이다. ex) DATABASE_TABLENAME
속성 설명 name JPA에서 사용할 엔티티 이름을 지정한다. 보통 기본값인 클래스의 이름을 사용 //DB엔티티임을 알려준다. @Entitiy //DB에서 table의 이름은 USER임을 명시적으로 알려준다. @Table(name="USER") public class User{ //PK를 의미한다. @id private int no; ... }
@Table
-
Entity와 매핑할 테이블을 지정한다. @Table은 생략이 가능한데, 생략한다면 매핑한 엔티티 이름을 테이블의 이름으로 사용한다.
속성 설명 name 매핑할 테이블 이름 catalog catalog 기능이 있는 데이터베이스에서 catalog를 매핑한다 schema schema 기능이 있는 데이터베이스에서 schema를 매핑한다 uniqueConstraints (DDL) DDL 생성 시에 유니크 제약조건을 만든다. 2개 이상의 복합 유니크 제약조건도 만들 수 있다. 참고로 이 기능은 스키마 자동 생성 기능을 사용해서 DDL을 만들 때만 사용된다.
@Column
-
@Column 어노테이션은 데이터베이스의 테이블에 있는 컬럼과 일대일로 매칭되기 때문에 Entity 클래스안에 내부변수로 정의한다.
-
테이블에 id, name, size 컬럼 3개가 있다면 각각 @Column 어노테이션을 작성한다.
다만 의도적으로 필요하지 않은 컬럼은 @Column을 작성하지 않아도 된다. -
별다른 옵션을 설정하지 않는다면 @Column을 생략해도 상관없다. 즉, Entity 클래스에 정의된 모든 내부변수는 기본적으로 @Column 어노테이션을 포함하고 있다고 생각하면 된다.
-
Spring Boot의 Spring.jpa.hibernate.ddl-auto 설정이 create 또는 update로 되어 있을 경우
-
create : 최초 한번 컬럼 생성
-
update : Entity클래스에 있지만 테이블에 존재하지 않는 컬럼을 추가로 생성
* 컬럼의 데이터 타입이 변경 되었거나 길이가 변경 되었을 때 자동으로 데이터베이스에 반영해주지 않는다.*
때문에 속성이 변경되면 기존 테이블을 drop하고 create 하거나 개별로 alter table을 통해 직접적으로 DDL구문을 적용 해야한다.
-
-
@Column도 @Entity 어노테이션과 동일하게 name 속성을 명시하지 않으면 Entity클래스에 정의한 컬럼 변수의 이름으로 생성이 된다.
그렇기 때문에 CamelCase로 작성된 컬럼 변수가 있다면 UnderScore 형식으로 작성해야 한다.
-
@Column은 length 속성을 통해 길이를 명시 할 수 있다.
- length 속성이 없다면 기본 길이인 255가 지정(문자열인 경우)
- 큰 숫자를 표현하는 BigDecimal일 경우 precision, scale로 최대 길이를 지정
속성 설명 name 필드와 매핑할 테이블의 컬럼 이름 insertable 엔티티 저장시 이 필드도 같이 저장한다. false로 설정하면 데이터베이스에 저장하지 않는다. 읽기 전용일때 사용한다. updatable 위와 동일한 하지만 수정일때 해당 된다. nullable(DDL) null 값 허용 여부를 설정한다. false일 경우 DDL생성시 not null 제약조건이 된다. unique(DDL) 한 컬럼에 간단히 유니크 제약 조건을 걸 때 사용한다. 만약 두개 이상 걸고 싶다면 클래스 레벨에서 @Table.uniqueConstraints를 사용해야 한다. columnDefinition(DDL) 데이터베이스 컬럼 정보를 직접 줄 수 있다. length 문자 길이 제약 조건이다. String만 해당된다. precision, scale(DDL) BigDecimal 타입에서 사용한다.(BigInteger 가능) precision은 소수점을 포함한 전체 자리수이고, scale은 소수점 자릿수이다. double랑 float타입에는 적용 되지 않는다. @Column private String name; //@Column 생략 pirvate String description; //컬럼명은 user_pwd임을 명시적으로 알려줌 @Column(name="user_pwd") private String password; //총 10자리, 소수점 자릿수는 3 @Column(precision=10, scale=3) pirvate BigDecimal amount; //생성, 수정시 DB에 저장하지 않는다. 읽기 전용 @Column(insertable = false, updateble = false) private LocalDateTime createDate;
@Id
-
데이터 베이스의 Primary Key를 의미한다.
-
데이터베이스는 유일한 키값을 기준으로 질의를 받고 데이터를 추출해 결과셋을 반환한다. 테이블 상에 PK가 없는 테이블도 있지만 대부분 반드시 PK가 존재한다.
-
JPA Entity 클래스 상에 해당 PK를 명시적으로 표시해야하며 그것을 @Id 어노테이션을 이용해 PK임을 알린다.
@Id @Column(name="user_id") private String userId;
@GeneratedValue
-
PK 컬럼의 데이터 형식은 정해져 있지 않으나 구분이 가능한 유일한 값을 가지고 있어야 하고 데이터 경합으로 인해 발생되는 deadlock 같은 현상을 방지하기 위해 대부분 BigInteger (java의 Long)을 사용한다.
-
String 형태의 고정된 키 값을 직접적으로 생성해 관리도 하지만, 중요한 점은 대량의 요청이 유입 되더라도 중복과 deadlock[^1]이 발생 되지 않을 만큼 키값이 빨리 생성되고 안전하게 관리 되어야 한다는 점이다.
-
가장 보편적으로 사용되는 MySQL, ORACLE에는 Long 타입의 키값을 생성하는 방식이 조금 다르다.
-
mySQL : Auto increment방식
-
숫자형의 PK컬럼 속성을 auto increment로 지정하면 자동으로 새로운 레코드가 생성될 때마다 마지막 PK값에서 자동으로 +1해주는 방식
-
이를 위해 @GeneratedValue 어노테이션의 strategy 속성을 GenerationType.IDENTITY로 지정해 auto increment 컬럼인 것을 EntityManager에 알린다.
@Id @GeneratedValue(Strategy = GenerationType.IDENTITY) private Long uid;
-
-
-
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 uid;
-
@EmbeddedId
-
일반적인 경우 PK를 단일 @Id로 구성하지만 경우에 따라서 복합키로서 테이블의 PK를 정의하기도 한다.
-
복합키는 두개 이상의 @Id로 구성이 되는데 이를 직접 Entity에 정의하는 것이 아니라 별도의 Value를 사용해 복합키를 정의한다.
- 먼저 Value를 생성하고 @Embeddable 어노테이션을 이용해 이 Value가 Entity에 삽입이 가능함을 명시한다.
- Entity에서는 @EmbeddedId 어노테이션을 이용해 이 entity에 해당 Value를 PK로 사용한다고 지정한다.
//복합키를 만든다. @Embeddable public class aOnizationKey implements Serializable{ @Column(name="a_code") pirvate String aCode; @Column(name="b_code") private String bCode; }
@Entity(name="a_organization")
public class aOrganization{
//해당 entity에서 복합키를 PK로 사용하겠다고 표시한다.
@EmbeddedId
protected OrganizationKey aOrgKey;
}
## @Enumerated
* @Enumerated 어노테이션은 java **enum**[^2]형태로 되어 있는 미리 정의되어 있는 코드 값이나 구분 값을 데이터 타입으로 사용하고 할 때 사용된다.
| 속성 | 설정 |
| :------------------- | :-------------------------------------------------- |
| **EnumType.ORDINAL** | enum 객체에 정의된 순서가 컬럼의 값으로 사용(index) |
| **EnumType.STRING** | enum의 문자열 자체가 컬럼의 값으로 사용 |
```java
enum Flag{yes, no} //내부적으로 클래스화 되어 있음
@Enumerated(EnumType.ORDINAL)
@Column(name="access_flag")
private Flag accessFlag; //index값 0과 1이 저장되어 있다.
@Enumerated(EnumType.STRING)
@Column(name="use_flag")
private Flag useFlag; //"yes", "no"가 string으로 들어가 있다.
@Transient
-
Entity 객체에 속성으로 지정되어 있지만 데이터베이스상에 필요없는 속성에 사용
-
@Transient 어노테이션을 이용해 '해당 속성을 데이터베이스에서 이용하지 않겠다'라고 정의
-
@Transient가 선언 되어 있으면 Entity객체에 임시로 값을 담는 용도로 이용이 가능하다.
@Transient private String tmp;
각주
[^1]: DB에서의 deadlock
- 동일한 시점에 요청이 유입 되었을 때 데이터베이스는 테이블 혹은 레코드에 lock을 걸어 데이터가 변경되지 않도록 막고 작업을 진행한다.
- 이때 A요청이 K테이블의 값을 변경하고 lock상태에서 J테이블을 사용하려고 하는 상태이고
- B요청이 J테이블의 값을 변경하고 lock상태에서 K테이블을 사용하려고 하면 DB는 우선순위를 판단 할 수가 없다.
- 따라서 DeadLock이 걸리고 강제 restart를 통해 커넥션을 초기화 해야한다.
[^2]: java에서 사용하는 열거형 타입