본문 바로가기

Spring Boot

[JPA] JPA 기본 사용 문법

@Entity


@Entity 어노테이션은 데이터베이스의 테이블과 일대일로 매칭 되는 객체 단위이며 Entity 객체의 인스턴스 하나가 테이블에서 하나의 레코드 값을 의미합니다. 그래서 객체의 인스턴스를 구분하기 위한 유일한 키값을 가지는데 이것은 테이블 상의 Primary Key 와 같은 의미를 가지며 @Id 어노테이션으로 표기됩니다.

 

이때 명시적으로 @Entityname 속성을 이용해 데이터베이스 상의 실제 테이블 명칭을 지정하지 않는다면 Entity 클래스의 이름 그대로 CamelCase 를 유지한 채 테이블이 생성되기 때문에 테이블 이름을 명시적으로 작성하는 것이 관례입니다. 왜냐하면 데이터베이스 상에서는 UnderScore 가 원칙이기 때문입니다.

 

@Entity
public class Organization {
	...
}

@Entity(name = "emplyee")
public Class Employee {
	...
}

@Entity(name = "employee_address")
public class EmployeeAddress {
	...
}

 

@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.hibernamte.ddl-auto 설정이 create 혹은 update 로 되어 있을 경우 create 일 때는 최초에 한 번 칼럼이 생성되고, update 일 때는 Entity 클래스에 있지만 해당 테이블에 존재하지 않는 칼럼을 추가도 생성해 줍니다. 하지만 칼럼의 데이터 타입이 변경되었거나 길이가 변경되었을 때 자동으로 데이터베이스에 반영을 해주지는 않기 때문에 속성이 변경되면 기존 테이블을 drop 후 새롭게 create 하던지 개별로 alter table 을 통해 직접 ddl 구문을 적용하는 것이 좋습니다.

 

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

 

데이터베이스 상에서 칼럼은 실제 데이터가 가질 수 있는 최대 길이를 가지게 되는데 이것은 데이터베이스의 데이터를 효율적으로 관리하기 위함입니다. @Column 에 length 속성으로 길이를 명시할 수 있습니다. 만약 length 속성이 없다면 기본 길이인 255가 지정됩니다. 이것은 문자열 형태인 테이터에만 해당되는 속성입니다.

 

@Column
private String code;

@Column(length = 100)
private String name;

// @Column 은 생략이 가능합니다.
private String description;

@Column
private Integer size;

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

@Id


@Id 어노테이션은 JPA에서 Entity 클래스 상에 PK를 명시할 때 사용하는 어노테이션입니다. 이것을 이용해 PK 임을 지정합니다.

 

@Id
@Column
private String code;

 

@GeneratedValue


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

 

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

 

deadlock

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

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

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

 

MySql 의 auto increment 방식은 새로운 레코드가 생성될 때마다 PK 값에 자동으로 +1 을 해주는 방식입니다. 이를 위해 @GeneratedValue 어노테이션의 strategy 속성을 GenerationType.IDENTITY 로 지정해 auto increment 칼럼인 것을 EntityManager 에 알려 줍니다. 

 

이때 자동으로 생성되는 값을 가지는 PK 칼럼의 이름은 명시적으로 id 로 지정하는 것이 관례입니다.

 

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

 

 

@EmbeddedId


일반적인 경우에는 PK 를 단일 @Id 로 구성하지만 경우에 따라서 복합키로서 테이블의 PK 를 정의하기도 합니다. 복합키는 두 개 이상의 @Id 로 구성이 되는데 이를 직접 Entity 에 정의하는 것이 아니라 별도의 Value 를 사용해 복합키를 정의합니다.

 

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

 

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

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

 

@Transient


만약 Entity 객체에 속성으로 지정되어 있지만 데이터베이스 상에 필요 없는 속성이라면 @Transient 어노테이션을 이용해서 '해당 속성을 데이터베이스에서 이용하지 않겠다'라고 정의합니다. 이렇게 하면 해당 속성을 Entity 객체에 임시로 값을 담는 용도로 사용이 가능해집니다.

 

@Transient
private String tempValue;